import json
import os
import shutil
from datetime import datetime
from functools import partial

from PyQt6.QtGui import QCursor, QPainter, QTextOption, QTextLayout, QColor, QPixmap
from matplotlib.figure import Figure
from sklearn.metrics import mean_squared_error

from again.Data_Management_page.DataManagerwidget_new2 import Ui_DataManagerwidget
import picture.resources_rc
import sys
from PyQt6 import QtWidgets, QtCore, uic, QtGui
from PyQt6.QtWidgets import QTableWidget, QVBoxLayout, QLabel, QStackedWidget, QWidget, QApplication, QFileDialog, \
    QMessageBox, QTableWidgetItem, QInputDialog, QPushButton, QScrollArea, QHBoxLayout, QMenu, QDialog, QSizePolicy, \
    QRadioButton, QLineEdit, QListWidgetItem, QFrame, QButtonGroup, QCheckBox, QHeaderView, QComboBox, QSpacerItem, \
    QSpinBox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
from PyQt6.QtCore import Qt, QTimer, QCoreApplication, QEvent, pyqtSignal, QObject, QDateTime
import pandas as pd
from matplotlib.animation import FuncAnimation
from TypeSetting_main import TypeSettingWindow
import concurrent.futures
from joblib import Parallel, delayed
from again.Train.GradientBoostingRegressor import GradientBoostingModel
from again.Train.SVM import SupportVectorMachineModel
from again.Train.Ridge import RidgeRegressionModel
from again.Train.Reverse import CompositionPredictorThread
from again.Inference.gb_inference import ModelInference

class TrainModelDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("训练模型")

        # 设置对话框布局
        layout = QVBoxLayout()

        # 名称
        self.name_label = QLabel("名称:")
        self.name_input = QLineEdit()
        layout.addWidget(self.name_label)
        layout.addWidget(self.name_input)

        # 类型
        self.type_label = QLabel("类型:")
        self.type_input = QLineEdit()
        layout.addWidget(self.type_label)
        layout.addWidget(self.type_input)

        # 子模型说明
        self.submodel_label = QLabel("子模型说明:")
        self.submodel_input = QLineEdit()
        layout.addWidget(self.submodel_label)
        layout.addWidget(self.submodel_input)

        # 确定和取消按钮
        button_layout = QHBoxLayout()
        self.ok_button = QPushButton("确定")
        self.cancel_button = QPushButton("取消")

        button_layout.addWidget(self.ok_button)
        button_layout.addWidget(self.cancel_button)
        layout.addLayout(button_layout)

        # 设置主布局
        self.setLayout(layout)

        # 连接按钮信号
        self.ok_button.clicked.connect(self.accept)
        self.cancel_button.clicked.connect(self.reject)

    def get_data(self):
        return {
            "name": self.name_input.text(),
            "type": self.type_input.text(),
            "submodel": self.submodel_input.text(),
        }

# 项目数据文件
MODEL_FILE = 'model.json'

def save_projects(projects):
    with open(MODEL_FILE, 'w', encoding='utf-8') as f:
        json.dump(projects, f, indent=4, ensure_ascii=False)

def load_projects():
    if os.path.exists(MODEL_FILE):
        with open(MODEL_FILE, 'r', encoding='utf-8') as f:
            try:
                return json.load(f)
            except json.JSONDecodeError:
                return []
    return []



# 模型界面第一页模型管理表格类
class ModelTableProjectManager:
    def __init__(self, table_widget):
        self.table_widget = table_widget
        self.selected_row = None
        self.filtered_projects = []  # 存储从JSON中获取的数据
        self.radio_button_group = QButtonGroup()
        self.radio_button_group.setExclusive(True)  # 设置为互斥
    def setup_modelTable(self):
        # 禁用列宽调整
        header = self.table_widget.horizontalHeader()
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)

        # 设置列宽
        self.set_column_widths([50, 50, 150, 60, 150, 170, 100, 250, 100, 125])

    def set_column_widths(self, widths):
        for i, width in enumerate(widths):
            self.table_widget.setColumnWidth(i, width)

    def load_model_table(self, filtered_projects):
        self.table_widget.setRowCount(0)
        self.filtered_projects = filtered_projects

        for idx, project in enumerate(self.filtered_projects):
            self.add_modelData_to_table(idx, project)
            self.add_radio_button_to_table(idx)  # 为每一行添加一个单选按钮

        # 设置默认选中项
        for idx, project in enumerate(self.filtered_projects):
            if project["state"] == "已创建":
                widget = self.table_widget.cellWidget(idx, 0)  # 获取嵌套的 QWidget
                if widget:
                    # 从 QWidget 中查找 QRadioButton
                    radio_button = widget.findChild(QRadioButton)
                    if radio_button:
                        radio_button.setChecked(True)  # 设置为选中状态

    def add_radio_button_to_table(self, row):
        # 创建 QRadioButton
        radio_button = QRadioButton()
        # 将 radio_button 添加到 QButtonGroup 中进行管理
        self.radio_button_group.addButton(radio_button, row)
        # 创建一个 QWidget 并设置布局
        widget = QWidget()
        layout = QVBoxLayout()
        layout.addWidget(radio_button)
        # 设置布局的上下左右边距为 0
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setAlignment(Qt.AlignmentFlag.AlignCenter)  # 设置布局中心对齐
        widget.setLayout(layout)

        # 将 QWidget 设置到表格单元格中
        self.table_widget.setCellWidget(row, 0, widget)
    # def add_radio_button_to_table(self, row):
    #     radio_button = QRadioButton()
    #     radio_button.toggled.connect(self.on_radio_button_toggled)
    #     self.table_widget.setCellWidget(row, 0, radio_button)

    def add_modelData_to_table(self, idx, project):
        row_position = self.table_widget.rowCount()
        self.table_widget.insertRow(row_position)

        # 添加数据到各列
        self.add_item(row_position, 1, str(idx + 1))  # 序号
        # self.add_item(row_position, 2, project["ID"])
        #
        # self.add_item(row_position, 3, project["name"])
        # self.add_item(row_position, 4, project["type"])
        # self.add_item(row_position, 5, project["created_time"])
        # self.add_item(row_position, 6, project["state"])
        # self.add_item(row_position, 7, project["submodel"])
        # self.add_item(row_position, 8, project["train_set"])
        # self.add_item(row_position, 9, project["Validation_set"])

        # 如果字段是列表，使用 join 方法将其转换为字符串
        self.add_item(row_position, 2, project["ID"])
        # self.add_item(row_position, 2, ", ".join(project["submodel_list"]))  # submodel_list 列
        self.add_item(row_position, 3, project["name"])  # 名称列
        self.add_item(row_position, 4, project["type"])  # 类型列
        self.add_item(row_position, 5, project["created_time"])  # 创建时间列
        self.add_item(row_position, 6, project["state"])  # 状态列
        self.add_item(row_position, 7, ", ".join(project["submodel"]))  # 子模型列

        # 检查 train_set 和 Validation_set 是否为空，或将列表转换为字符串
        self.add_item(row_position, 8, project["train_set"] if project["train_set"] else "无")  # 训练集列
        self.add_item(row_position, 9, ", ".join([f"{val:.2f}" for val in project["Validation_set"]]))  # 验证集列

    def add_item(self, row, col, text):
        item = QtWidgets.QTableWidgetItem(text)
        item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)  # 设置内容居中对齐
        self.table_widget.setItem(row, col, item)

    def on_radio_button_toggled(self):
        # 每次有新的按钮被选中时取消其他按钮的选中状态
        for row in range(self.table_widget.rowCount()):
            radio_button = self.table_widget.cellWidget(row, 0)
            if radio_button and radio_button.isChecked():
                self.selected_row = row
                break
# 模型界面第二页数据设置输入设置类（推理）
class DataSettingTableReasoningInputManager:
    def __init__(self, table_widget, features):
        self.table_widget = table_widget
        self.features = features
    def set_column_widths(self, widths):
        for i, width in enumerate(widths):
            self.table_widget.setColumnWidth(i, width)
    def setup_table(self):
        """设置表格样式并填充数据."""
        self.table_widget.setColumnCount(4)  # 设置四列：属性名、最小值、最大值、取值步长
        self.table_widget.setHorizontalHeaderLabels(['属性名', '最小值', '最大值', '取值步长'])  # 设置表头

        # 禁用列宽调整
        header = self.table_widget.horizontalHeader()
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
        # 设置列宽
        self.set_column_widths([350, 270, 270, 270])

        # 清空表格内容
        self.table_widget.setRowCount(0)

        # 将 features 数组中的每个属性名填入第一列
        for i, feature in enumerate(self.features):
            self.table_widget.insertRow(i)  # 插入新的一行

            # 第一列：属性名（不可编辑）
            item = QTableWidgetItem(feature)
            item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
            item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)  # 设置为不可编辑
            self.table_widget.setItem(i, 0, item)

            # 第二列：最小值（可编辑的 QLineEdit）
            min_value = QLineEdit()
            self.table_widget.setCellWidget(i, 1, min_value)

            # 第三列：最大值（可编辑的 QLineEdit）
            max_value = QLineEdit()
            self.table_widget.setCellWidget(i, 2, max_value)

            # 第四列：取值步长（可编辑的 QLineEdit）
            step_value = QLineEdit()
            self.table_widget.setCellWidget(i, 3, step_value)

    def get_param_ranges(self):
        """获取手动输入的数据并存储为字典。"""
        param_ranges = {}
        for row in range(self.table_widget.rowCount()):
            feature_name = self.table_widget.item(row, 0).text()  # 获取属性名
            min_value = self.table_widget.cellWidget(row, 1).text()  # 获取最小值
            max_value = self.table_widget.cellWidget(row, 2).text()  # 获取最大值
            step_value = self.table_widget.cellWidget(row, 3).text()  # 获取步长

            # 将获取的数据存入字典，并将数值转换为浮点数或整数
            param_ranges[feature_name] = (
                float(min_value) if min_value else None,
                float(max_value) if max_value else None,
                float(step_value) if step_value else None,
            )

        return param_ranges

# 模型界面第二页数据设置输出设置类（推理）
class DataSettingTableReasoningOutputManager:
    def __init__(self, table_widget, target_data):
        self.table_widget = table_widget
        self.target_data = target_data

    def set_column_widths(self, widths):
        """设置列宽."""
        for i, width in enumerate(widths):
            self.table_widget.setColumnWidth(i, width)

    def setup_table(self):
        """设置表格样式并填充数据."""
        self.table_widget.setColumnCount(3)  # 设置三列：目标名、搜索范围、目标范围
        self.table_widget.setHorizontalHeaderLabels(['目标名', '搜索范围', '目标范围'])  # 设置表头

        # 禁用列宽调整
        header = self.table_widget.horizontalHeader()
        header.setSectionResizeMode(self.table_widget.horizontalHeader().ResizeMode.Fixed)

        # 设置列宽
        self.set_column_widths([350, 500, 300])

        # 清空表格内容
        self.table_widget.setRowCount(0)

        # 将 target_data 数组中的每个目标填入表格
        for i, target in enumerate(self.target_data):
            self.table_widget.insertRow(i)  # 插入新的一行

            # 第一列：目标名（不可编辑）
            item = QTableWidgetItem(target)
            item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
            item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)  # 设置为不可编辑
            self.table_widget.setItem(i, 0, item)

            # 第二列：搜索范围（包含 QCheckBox 和 QLabel）
            checkbox = QCheckBox()
            label = QLabel("达到目标范围")
            label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            container_widget = QWidget()
            layout = QHBoxLayout(container_widget)
            layout.addWidget(checkbox)
            layout.addWidget(label)
            layout.setContentsMargins(0, 0, 0, 0)  # 设置布局边距为 0
            self.table_widget.setCellWidget(i, 1, container_widget)

            # 第三列：目标范围（包含两个 QLineEdit）
            min_value = QLineEdit()
            max_value = QLineEdit()
            range_container_widget = QWidget()
            range_layout = QHBoxLayout(range_container_widget)
            range_layout.addWidget(min_value)
            range_layout.addWidget(QLabel("～"))
            range_layout.addWidget(max_value)
            range_layout.setContentsMargins(0, 0, 0, 0)  # 设置布局边距为 0
            self.table_widget.setCellWidget(i, 2, range_container_widget)

    def get_target_ranges(self):
        """获取手动输入的数据并存储为字典。"""
        param_ranges = []
        for row in range(self.table_widget.rowCount()):
            feature_name = self.table_widget.item(row, 1).text()  # 获取搜索范围属性
            min_value = self.table_widget.cellWidget(row, 2).text()  # 获取最小值

            # 将获取的数据存入字典，并将数值转换为浮点数或整数
            param_ranges[feature_name] = (
                float(min_value) if min_value else None,
            )

        return param_ranges


# 模型界面第三页参数设置子模型选择类
class SubModelTableManager:
    def __init__(self, table_widget, main_class_ref):
        self.table_widget = table_widget
        self.main_class_ref = main_class_ref  # 保留对 MainClass 的引用
        self.selected_row = None
        # 创建 QButtonGroup 来管理单选按钮的互斥性
        self.radio_button_group = QButtonGroup()
        self.radio_button_group.setExclusive(True)  # 设置为互斥
        self.hyperparameter_settings = {}  # 用于存储每行的超参数设置

    def setup_modelTable(self, width_list):
        # 禁用列宽调整
        header = self.table_widget.horizontalHeader()
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
        # 设置列宽
        self.set_column_widths(width_list)

    def set_column_widths(self, widths):
        for i, width in enumerate(widths):
            self.table_widget.setColumnWidth(i, width)

    def load_model_table(self, subModel):
        self.table_widget.setRowCount(0)  # 清空表格

        for idx, project in enumerate(subModel):
            self.add_modelData_to_table(idx, project)
            self.add_radio_button_to_table(idx)  # 为每一行添加一个单选按钮

        # 设置表格高度
        self.adjust_table_height()

    def add_radio_button_to_table(self, row):
        check_box = QCheckBox()  # 创建复选框
        check_box.setChecked(False)  # 默认不选中

        # 创建一个 QWidget 并设置布局
        widget = QWidget()
        layout = QVBoxLayout()
        layout.addWidget(check_box)

        # 设置布局的上下左右边距为 0
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setAlignment(Qt.AlignmentFlag.AlignCenter)  # 设置布局中心对齐
        widget.setLayout(layout)

        # 将 QWidget 设置到表格单元格中
        self.table_widget.setCellWidget(row, 0, widget)

    def add_modelData_to_table(self, idx, project):
        row_position = self.table_widget.rowCount()
        self.table_widget.insertRow(row_position)

        # 添加数据到各列
        self.add_item(row_position, 1, project["name"])
        # self.add_item(row_position, 2, str(idx + 1))

        # 添加超参数设置按钮
        button = QPushButton("超参数设置")

        # 传递当前行号和数据给按钮点击事件
        button.clicked.connect(lambda: self.open_hyperparameter_dialog(idx, project))
        self.table_widget.setCellWidget(row_position, 2, button)

    def open_hyperparameter_dialog(self, idx, project):
        try:
            dialog = HyperparameterDialog()
            if dialog.exec() == QDialog.DialogCode.Accepted:
                param1 = dialog.param1_input.text()
                param2 = dialog.param2_input.text()
                param3 = dialog.param3_input.text()
                param4 = dialog.param4_input.text()
                param5 = dialog.param5_input.text()
                param6 = dialog.param6_input.text()
                param7 = dialog.param7_input.text()

                # 将参数存储到字典中，使用 idx 作为键
                self.hyperparameter_settings[idx] = {
                    'param1': param1,
                    'param2': param2,
                    'param3': param3,
                    'param4': param4,
                    'param5': param5,
                    'param6': param6,
                    'param7': param7,
                }

                # 将超参数数据存储到 主类 的变量中
                self.main_class_ref.hyperparameter_data["Hyperparameter"+str(idx)] = self.hyperparameter_settings[idx]
                print(f"为第 {idx + 1} 行的项目 '{project['name']}' 设置了超参数: {param1}, {param2}, {param3}, {param4}, {param5}, {param6}, {param7}")

                # 主类中的hyperparameter_data
                print("当前的 hyperparameter_data:", self.main_class_ref.hyperparameter_data)
        except Exception as e:
            print(f"处理对话框时出现异常: {e}")

    def get_hyperparameter_settings(self):
        # 获取指定行的超参数设置
        return self.hyperparameter_settings
    def add_item(self, row, col, text):
        item = QtWidgets.QTableWidgetItem(text)
        item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)  # 设置内容居中对齐
        self.table_widget.setItem(row, col, item)

    def on_radio_button_toggled(self):
        # 每次有新的按钮被选中时取消其他按钮的选中状态
        for row in range(self.table_widget.rowCount()):
            radio_button = self.table_widget.cellWidget(row, 0)
            if radio_button and radio_button.isChecked():
                self.selected_row = row
                break

    def adjust_table_height(self):
        # 获取表头高度
        header_height = self.table_widget.horizontalHeader().height()

        # 计算所有行的总高度
        total_height = header_height
        for row in range(self.table_widget.rowCount()):
            total_height += self.table_widget.rowHeight(row)

        # 设置表格高度
        self.table_widget.setFixedHeight(total_height)

# 模型界面第三页参数设置子模型选择类(推理)

class SubModelTableManager_res:
    def __init__(self, table_widget, submodel_type, main_instance):
        self.table_widget = table_widget  # 保存传入的 tableWidget
        self.submodel_type = submodel_type  # 保存传入的 submodel 类型列表
        self.main_instance = main_instance  # 主函数的实例，用于存储选中行号
        self.selected_button = None  # 当前选中的按钮

    def setup_table(self):
        # 设置表格的列数、列标题和行数
        self.table_widget.setColumnCount(2)
        headers = ["选择模型", "子模型类型"]
        self.table_widget.setHorizontalHeaderLabels(headers)
        self.table_widget.setRowCount(len(self.submodel_type))

        for row, model_type in enumerate(self.submodel_type):
            # 在第一列添加 QRadioButton
            radio_button = QRadioButton()
            radio_button.clicked.connect(lambda _, r=row, btn=radio_button: self.set_selected_model(r, btn))

            # 默认选中第一行
            if row == 0:
                radio_button.setChecked(True)
                self.selected_button = radio_button
                self.main_instance.row = row  # 将默认选中的行号存储到 main_instance

            widget = QWidget()
            layout = QHBoxLayout(widget)
            layout.addWidget(radio_button)
            layout.setAlignment(radio_button, Qt.AlignmentFlag.AlignCenter)
            layout.setContentsMargins(0, 0, 0, 0)
            self.table_widget.setCellWidget(row, 0, widget)

            # 在第二列填入子模型类型，并设置文本居中
            item = QTableWidgetItem(model_type)
            item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
            self.table_widget.setItem(row, 1, item)

        # 自适应列宽
        self.table_widget.resizeColumnsToContents()

    def set_selected_model(self, row, button):
        # 如果之前有选中的按钮，取消其选中状态
        if self.selected_button:
            self.selected_button.setChecked(False)

        # 更新当前选中的按钮和行号
        self.selected_button = button
        button.setChecked(True)
        self.main_instance.row = row

# 模型界面第四页数据集结论表格类
class ModelTableResultManager:
    def __init__(self, main_window):
        self.main_window = main_window
        self.table_widget = self.main_window.findChild(QTableWidget, "tableWidgetConclusion")
        # 持久化存储模型结果
        self.model_results_list = []

        # 保存每行按钮的点击状态（只允许点击一次），初始为 False（未保存）
        # self.row_save_states = {}

    def handle_save_button(self, model_result):

        # # 检查该行的按钮是否已经被点击
        # if self.row_save_states.get(row_position, False):
        #     QMessageBox.warning(self.table_widget, "提示", "您已保存了模型")
        #     return


        model_type = model_result.get('model_type', 'N/A')
        cv_mean_score = model_result.get('cv_mean_score', 'N/A')
        model_name = model_result.get('model_name', 'N/A')
        model_filename = model_result.get('model_filename', 'N/A')
        print("model_filename:", model_filename)
        features = model_result.get('features', 'N/A')
        target = model_result.get('target', 'N/A')

        json_file_path = './model.json'
        # 读取现有的 model.json 文件
        with open(json_file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)

        # 查找 "state" 为 "已创建" 的条目
        created_entry = None
        for entry in data:
            if entry.get("state") == "已创建":
                created_entry = entry  # 保存该条目，以便后续使用
                break  # 找到并保存后退出循环

        # 如果找到了已创建条目，构造新的条目
        if created_entry:
            new_entry = {
                "ID": created_entry["ID"],  # 新的模型 ID
                # "name": task_name,
                "name": created_entry["name"],  # 从已创建条目获取名称
                "type": model_type,  # 使用新的模型类型
                "created_time": created_entry["created_time"],  # 从已创建条目获取创建时间
                "state": "已完成",  # 新的状态
                "submodel": model_name,  # 新的子模型
                "submodel_list": model_filename,
                "train_set": created_entry["train_set"],  # 从已创建条目获取训练集
                # "Validation_set": f"{cv_mean_score:.3f}",  # 新的验证集
                "Validation_set":cv_mean_score,
                "parameter_setting": created_entry["parameter_setting"],
                "features": features,  # 模型训练属性
                "target":target,
            }

            # 将新的条目添加到数据列表中
            data.append(new_entry)

        # 打印修改后的数据到控制台
        print("修改后的 model.json 数据:")
        # print(json.dumps(data, ensure_ascii=False, indent=4))

        # 将更新后的数据写回到 JSON 文件
        with open(json_file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=4)
        # # 标记该行按钮已经被点击
        # self.row_save_states[row_position] = True
    def update_table_pos(self, model_results):
        # 将新的模型结果添加到持久化列表
        self.model_results_list.append(model_results)

        # 设置表头
        headers = [
            "序号", "任务类型", "寻优方式", "模型类型",
            "训练时间", "训练集R²", "测试集R²",
            "训练集MSE", "测试集MSE",
            "交叉验证得分", "平均得分"
            # "梯度提升特征重要性"
        ]
        self.table_widget.setColumnCount(len(headers))
        self.table_widget.setHorizontalHeaderLabels(headers)

        # 获取当前表格的行数（即已有的行数，用于递增序号）
        current_row_count = self.table_widget.rowCount()

        # 插入新数据
        row_position = current_row_count  # 新行的位置是当前行数
        self.table_widget.insertRow(row_position)

        # 设置序号（从1开始）
        self.table_widget.setItem(row_position, 0, QTableWidgetItem(f"{row_position + 1}"))

        # 填充其他数据，保留五位小数
        self.table_widget.setItem(row_position, 1, QTableWidgetItem(f"{model_results['task_type']}"))
        self.table_widget.setItem(row_position, 2, QTableWidgetItem(f"{model_results['model_type']}"))
        self.table_widget.setItem(row_position, 3, QTableWidgetItem(f"{model_results['model_name']}"))

        # 创建右对齐的 QTableWidgetItem 并设置对齐方式
        def right_aligned_item(text):
            item = QTableWidgetItem(text)
            item.setTextAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)  # 右对齐并垂直居中
            return item

        # 填充右对齐的列
        self.table_widget.setItem(row_position, 4, right_aligned_item(f"{model_results['train_time']:.3f}"))
        self.table_widget.setItem(row_position, 5, right_aligned_item(f"{model_results['r2_train']:.3f}"))
        self.table_widget.setItem(row_position, 6, right_aligned_item(f"{model_results['r2_test']:.3f}"))
        self.table_widget.setItem(row_position, 7, right_aligned_item(f"{model_results['mse_train']:.3f}"))
        self.table_widget.setItem(row_position, 8, right_aligned_item(f"{model_results['mse_test']:.3f}"))
        self.table_widget.setItem(row_position, 9, right_aligned_item(str(np.round(model_results['cv_scores'], 4))))
        self.table_widget.setItem(row_position, 10, right_aligned_item(f"{model_results['cv_mean_score']:.3f}"))

        # 在第12列添加保存按钮
        # self.save_button_clicked = False  # 初始化状态变量，表示按钮是否被点击
        # save_button = QPushButton("保存")
        # save_button.clicked.connect(lambda: self.handle_save_button(row_position))  # 点击按钮时触发保存逻辑
        # self.table_widget.setCellWidget(row_position, 11, save_button)
        # # 初始化该行的保存状态为 False
        # self.row_save_states[row_position] = False

        # 增加保存到json逻辑
        # self.handle_save_button(row_position)

        # 调整每一列的宽度，使其根据内容自动调整
        self.table_widget.resizeColumnsToContents()

        # 动态调整表格和父控件的高度
        self.adjust_table_height()

    def update_table_rev(self,attributes, targets,model_results,tolerance):
        # 将新的模型结果添加到持久化列表
        self.model_results_list.extend(model_results)  # 使用 extend 添加多个结果

        # 1. 设置表格的列数: 1列为序号 + 目标列数 + 属性列数
        num_columns = 1 + len(attributes) + len(targets) + len(targets)+1

        self.table_widget.setColumnCount(num_columns)

        # 2. 设置列标题: 序号 + 属性列 + 目标列
        headers = ['序号'] + [f"{i}" for i in attributes] + [f"{i}" for i in targets] + [f"{i}的误差百分比" for i in
                                                                                         targets] + ["是否符合要求"]
        self.table_widget.setHorizontalHeaderLabels(headers)

        # 获取当前表格的行数（即已有的行数，用于递增序号）
        initial_row_count = self.table_widget.rowCount()

        # 插入新数据
        for result in model_results:
            # 检查性能差异是否在误差范围内
            if not all(diff < tolerance for diff in result['performance_differences']):
                continue  # 如果不符合误差范围，跳过这行数据

            # 获取当前行数作为新行的位置
            row_position = self.table_widget.rowCount()
            self.table_widget.insertRow(row_position)

            # 设置序号（从1开始）
            self.table_widget.setItem(row_position, 0, QTableWidgetItem(f"{row_position + 1}"))

            # 填充预测成分
            for col_index, comp in enumerate(result['predicted_composition']):
                self.table_widget.setItem(row_position, col_index + 1, QTableWidgetItem(f"{comp:.2f}"))

            # 填充预测性能
            for col_index, perf in enumerate(result['predicted_performance']):
                self.table_widget.setItem(row_position, col_index + len(result['predicted_composition']) + 1,
                                          QTableWidgetItem(f"{perf:.2f}"))

            # 填充性能差异
            for col_index, diff in enumerate(result['performance_differences']):
                self.table_widget.setItem(
                    row_position,
                    col_index + len(result['predicted_composition']) + len(result['predicted_performance']) + 1,
                    QTableWidgetItem(f"{diff:.2f}")
                )

            # 显示“该性能组合符合误差范围”
            self.table_widget.setItem(row_position, len(headers) - 1, QTableWidgetItem("该性能组合符合误差范围"))

        # 检查是否插入了任何符合条件的行
        if self.table_widget.rowCount() == initial_row_count:
            # 弹出提示框
            QMessageBox.information(
                self.table_widget,
                "提示",
                "未找到符合误差范围的成分组合，请调整容忍范围或增加迭代次数。"
            )

        # 调整每一列的宽度，使其根据内容自动调整
        self.table_widget.resizeColumnsToContents()

        # 动态调整表格和父控件的高度
        self.adjust_table_height()
    def adjust_table_height(self):
        # 获取表头高度
        header_height = self.table_widget.horizontalHeader().height()

        # 计算所有行的总高度
        total_height = header_height
        for row in range(self.table_widget.rowCount()):
            total_height += self.table_widget.rowHeight(row)

        # 设置表格高度
        self.table_widget.setFixedHeight(total_height)

class ModelTableResultManager_res:
    def __init__(self, main_window):
        self.main_window = main_window
        self.table_widget = self.main_window.findChild(QTableWidget, "tableWidgetConclusion_res")

    def setup_table(self, targets, attributes, data_dict):
        # 1. 设置表格的列数: 1列为序号 + 目标列数 + 属性列数
        num_columns = 1 + len(targets) + len(attributes)
        self.table_widget.setColumnCount(num_columns)

        # 2. 设置列标题: 序号 + 目标列 + 属性列
        headers = ['序号'] + [f"{i}" for i in targets] + [f"{i}" for i in attributes]
        self.table_widget.setHorizontalHeaderLabels(headers)

        # 3. 设置行数: 根据传入数据字典的大小
        num_rows = len(data_dict)
        self.table_widget.setRowCount(num_rows)

        # 4. 填充表格数据
        for row_index, (key, value) in enumerate(data_dict.items()):
            # 获取目标值和属性值
            combination = value['combination']
            target = value['target']

            # 填充“序号”列
            self.table_widget.setItem(row_index, 0, QTableWidgetItem(str(row_index + 1)))

            # 填充目标列
            for target_index, target_value in enumerate(target):
                self.table_widget.setItem(row_index, 1 + target_index, QTableWidgetItem(str(round(target_value, 3))))

            # 填充属性列
            for attr_index, attr_value in enumerate(combination):
                self.table_widget.setItem(row_index, 1 + len(targets) + attr_index, QTableWidgetItem(str(attr_value)))

        # 自适应列宽
        self.table_widget.resizeColumnsToContents()

# 模型界面第四页loss曲线填充类
class ImageWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.current_image_path = None

    def paintEvent(self, event):
        if self.current_image_path:
            painter = QPainter(self)
            pixmap = QPixmap(self.current_image_path)
            if not pixmap.isNull():
                painter.drawPixmap(self.rect(), pixmap)
            else:
                print(f"加载图片失败: {self.current_image_path}")
        else:
            print("没有图片路径")

class ImageComboBoxController:
    def __init__(self, widget_loss_main):
        # 找到 QComboBox 实例
        self.combo_box = widget_loss_main.findChild(QComboBox, "comboBoxModelTypePic")
        self.combo_box.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)  # 自适应宽度
        # 设置大小策略，允许 QComboBox 在内容改变时调整大小
        # self.combo_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)

        self.image_widget = ImageWidget(widget_loss_main)
        self.image_widget.setFixedSize(950, 570)
        self.image_widget.setStyleSheet("background-color: white;")  # 设置背景颜色

        # 找到 widgetLossPic，并将 image_widget 设置为其子控件
        widget_loss_main.findChild(QWidget, "widgetLossPic").layout().addWidget(self.image_widget)

        # 字典存储下拉框选项和对应图片路径
        self.image_paths = {}
        self.current_image_path = None


    def connect_signals(self):
        """连接信号到槽函数"""
        if self.combo_box:
            connected = self.combo_box.currentIndexChanged.connect(self.on_combo_box_changed)
            print("信号已连接到 on_combo_box_changed" if connected else "信号未连接")
        else:
            print("未找到 comboBox")

    def on_combo_box_changed(self, index):
        """当下拉框改变时，调用该函数更新图片"""
        print(f"ComboBox index changed: {index}")
        current_text = self.combo_box.currentText()
        print(f"当前选项文本: {current_text}")
        self.update_image()

    def add_item(self, combo_box_text, image_path):
        """添加下拉框项和对应的图片路径"""
        print(f"添加下拉框项: {combo_box_text} -> {image_path}")
        self.combo_box.addItem(combo_box_text)
        self.image_paths[combo_box_text] = image_path
        print(f"当前 image_paths 字典: {self.image_paths}")


    def update_image(self):
        """根据当前下拉框的选项更新图片"""
        current_text = self.combo_box.currentText()
        print(f"当前下拉框选项: {current_text}")
        if current_text in self.image_paths:
            new_image_path = self.image_paths[current_text]
            if new_image_path != self.image_widget.current_image_path:
                print(f"新图片路径: {new_image_path}")
                self.image_widget.current_image_path = new_image_path
                self.image_widget.update()  # 请求重绘
            else:
                print("当前显示的图片已是最新图片")
        else:
            print(f"未找到下拉框选项: {current_text} 对应的路径")

    def initialize_combobox(self):
        """初始化下拉框，选择第二个项并更新图像"""
        if self.combo_box.count() > 0:
            self.combo_box.setCurrentIndex(0)  # 设置默认选择为第二项
            self.update_image()  # 更新图像
# 超参数设置
class HyperparameterDialog(QDialog):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("超参数设置")
        self.setFixedSize(300, 200)

        layout = QVBoxLayout()

        self.param1_input = QLineEdit(self)
        self.param1_input.setPlaceholderText("参数1")
        layout.addWidget(self.param1_input)

        self.param2_input = QLineEdit(self)
        self.param2_input.setPlaceholderText("参数2")
        layout.addWidget(self.param2_input)

        self.param3_input = QLineEdit(self)
        self.param3_input.setPlaceholderText("参数3")
        layout.addWidget(self.param3_input)

        self.param4_input = QLineEdit(self)
        self.param4_input.setPlaceholderText("参数4")
        layout.addWidget(self.param4_input)

        self.param5_input = QLineEdit(self)
        self.param5_input.setPlaceholderText("参数5")
        layout.addWidget(self.param5_input)

        self.param6_input = QLineEdit(self)
        self.param6_input.setPlaceholderText("参数6")
        layout.addWidget(self.param6_input)

        self.param7_input = QLineEdit(self)
        self.param7_input.setPlaceholderText("参数7")
        layout.addWidget(self.param7_input)

        button_layout = QHBoxLayout()
        self.confirm_button = QPushButton("确定", self)
        self.cancel_button = QPushButton("取消", self)

        button_layout.addWidget(self.confirm_button)
        button_layout.addWidget(self.cancel_button)
        layout.addLayout(button_layout)

        self.setLayout(layout)

        # 连接按钮的信号槽
        self.confirm_button.clicked.connect(self.on_confirm)
        self.cancel_button.clicked.connect(self.reject)

    def on_confirm(self):
        try:
            # 获取输入的参数值
            param1 = self.param1_input.text()
            param2 = self.param2_input.text()
            param3 = self.param3_input.text()
            param4 = self.param4_input.text()
            param5 = self.param5_input.text()
            param6 = self.param6_input.text()
            param7 = self.param7_input.text()


            # 在这里处理设置逻辑，比如保存到模型
            print(f"参数1: {param1}, 参数2: {param2}, 参数3: {param3}, 参数4: {param4},参数5: {param5}, 参数6: {param6},参数7: {param7}")

            self.accept()  # 关闭对话框
        except Exception as e:
            print(f"出现异常: {e}")
            # 在这里处理异常，比如显示错误消息
            self.reject()


# 模型界面按钮点击事件类
class ModelStepManager:
    def __init__(self, main_window):
        self.main_window = main_window
        self.parameter_data = {}  # 初始化一个属性来存储 JSON 数据
        self.target_data = []
        self.features_data = []
        self.submodel_data = []
        self.model_ID = ""
        self.model_type = ""
        self.param_ranges = {}  # 初始化一个存储输入属性存储范围字典
        self.row = None
        self.submodel_list = []
        self.submodel = ""
        self.current_step = 0

        # 逆向搜索的结果列表
        self.results = []

        # 获取标签和按钮
        self.labelModelManage = self.main_window.findChild(QLabel, "labelModelManage")
        # self.labelDataSetting = self.main_window.findChild(QLabel, "labelDataSetting")
        self.labelParameterSetting = self.main_window.findChild(QLabel, "labelParameterSetting")
        self.labelExperimentResult = self.main_window.findChild(QLabel, "labelExperimentResult")

        self.pBtnResetting = self.main_window.findChild(QPushButton, "pBtnResetting")
        self.pBtnModelBack = self.main_window.findChild(QPushButton, "pBtnModelBack")
        self.pBtnModelNext = self.main_window.findChild(QPushButton, "pBtnModelNext")
        self.pBtnAnalysis = self.main_window.findChild(QPushButton, "pBtnAnalysis")

        # 获取页面
        self.stacked_widget = self.main_window.findChild(QStackedWidget, "widgetBtnColumn")
        # 训练、微调的四个页面
        self.page_model_manage = self.stacked_widget.findChild(QWidget, "widgetModelManage")
        # self.page_data_setting = self.stacked_widget.findChild(QWidget, "widgetDataSettings")
        self.page_parameter_setting = self.stacked_widget.findChild(QWidget, "widgetParameterSettings")
        self.page_experiment_result = self.stacked_widget.findChild(QWidget, "widgetExperimentalResult")
        # 推理的四个页面
        # self.page_data_setting_res = self.stacked_widget.findChild(QWidget, "widgetDataSettings_res")
        self.page_parameter_setting_res = self.stacked_widget.findChild(QWidget, "widgetParameterSettings_res")
        self.page_experiment_result_res = self.stacked_widget.findChild(QWidget, "widgetExperimentalResult_res")

        # self.tableWidgetConclusion = self.page_experiment_result.findChild(QTableWidget, "tableWidgetConclusion")
        # 连接按钮的点击事件
        self.pBtnResetting.clicked.connect(self.reset)
        self.pBtnModelBack.clicked.connect(self.prev_step)
        self.pBtnModelNext.clicked.connect(self.next_step)
        self.pBtnAnalysis.clicked.connect(self.analyze)

        # 初始更新步骤标签和按钮
        self.update_step_labels()
        self.update_buttons()

    def update_step_labels(self):
        # 训练/微调路径的标签
        labels_train_adjust = [
            self.labelModelManage,  # 对应 step 0
            # self.labelDataSetting,  # 对应 step 1
            self.labelParameterSetting,  # 对应 step 2
            self.labelExperimentResult  # 对应 step 3
        ]

        # 推理路径的标签
        labels_reasoning = [
            self.labelModelManage,  # 对应 step 0
            # self.labelDataSetting,  # 对应 step 4
            self.labelParameterSetting,  # 对应 step 5
            self.labelExperimentResult  # 对应 step 6
        ]

        # 定义颜色：当前步骤为蓝色，已完成的步骤为绿色，未完成的为白色
        active_color = QColor("#00aaff")
        completed_color = QColor("#aaff00")
        future_color = QColor("#ffffff")

        # 更新训练/微调路径标签的颜色
        if 0 <= self.current_step <= 2:
            for i, label in enumerate(labels_train_adjust):
                if i == self.current_step:
                    color = active_color  # 当前步骤
                elif i < self.current_step:
                    color = completed_color  # 已完成的步骤
                else:
                    color = future_color  # 未完成的步骤
                label.setStyleSheet(f"background-color: {color.name()}")

        # 更新推理路径标签的颜色
        elif 3 <= self.current_step <= 4:
            for i, label in enumerate(labels_reasoning):
                if (i + 2) == self.current_step:  # 对应 step 3, 4
                    color = active_color
                elif (i + 2) < self.current_step:
                    color = completed_color
                else:
                    color = future_color
                label.setStyleSheet(f"background-color: {color.name()}")

        # 更新页面显示
        if self.current_step == 0:
            self.stacked_widget.setCurrentWidget(self.page_model_manage)
        # elif self.current_step == 1:
        #     self.stacked_widget.setCurrentWidget(self.page_data_setting)
        elif self.current_step == 1:
            self.stacked_widget.setCurrentWidget(self.page_parameter_setting)
        elif self.current_step == 2:
            self.stacked_widget.setCurrentWidget(self.page_experiment_result)
        # elif self.current_step == 4:
        #     self.stacked_widget.setCurrentWidget(self.page_data_setting_res)
        elif self.current_step == 3:
            self.stacked_widget.setCurrentWidget(self.page_parameter_setting_res)
        elif self.current_step == 4:
            self.stacked_widget.setCurrentWidget(self.page_experiment_result_res)

    def update_buttons(self):
        # 更新按钮状态
        self.pBtnResetting.setEnabled(self.current_step > 0)
        self.pBtnModelBack.setEnabled(self.current_step > 0)
        self.pBtnModelNext.setEnabled(self.current_step < 4 and self.current_step != 2)
        self.pBtnAnalysis.setEnabled(self.current_step == 2 or self.current_step == 4)

    def reset(self):
        self.current_step = 0
        self.update_step_labels()
        self.update_buttons()

    def prev_step(self):
        if self.current_step > 0 and self.current_step != 3:
            self.current_step -= 1
            self.update_step_labels()
            self.update_buttons()
        else:
            self.reset()
    def next_step(self):
        # 判断当前步骤是否为 0
        if self.current_step == 0:
            # 检查 tableWidget_model 中是否有选择的 radio button
            selected_row = None
            for row in range(self.main_window.ui.tableWidget_model.rowCount()):
                # 获取单元格中的 QWidget
                cell_widget = self.main_window.ui.tableWidget_model.cellWidget(row, 0)

                if cell_widget:
                    # 从 QWidget 中查找 QRadioButton
                    radio_button = cell_widget.findChild(QRadioButton)
                    if radio_button and radio_button.isChecked():
                        selected_row = row
                        # 打印选中行的所有数据
                        row_data = []
                        for col in range(self.main_window.ui.tableWidget_model.columnCount()):
                            item = self.main_window.ui.tableWidget_model.item(selected_row, col)
                            if item:
                                row_data.append(item.text())
                            else:
                                row_data.append('')  # 如果单元格为空，添加空字符串
                        self.model_ID = row_data[2]

                        # 选中 rBtnReasoning 时，跳转到 step 4
                        if self.main_window.ui.rBtnReasoning.isChecked():
                            print("selected_row is", selected_row, "Selected row data id:", row_data[2])
                            self.current_step = 3
                            # 当进入第 4 步时，填充页面4上的表格
                            # 获取features 数组填充输入属性搜索范围
                            self.features_data = self.get_ft_data_from_model(self.model_ID,0)  # 获取 features 数组
                            tb_input_features = DataSettingTableReasoningInputManager(self.main_window.ui.tableWidgetSearchRange,
                                                                             self.features_data)
                            tb_input_features.setup_table()
                            # 获取target 数组填充输出目标要求
                            self.target_data = self.get_ft_data_from_model(self.model_ID, 1)  # 获取 target 数组
                            # 使用 tableWidgetOutputParameters 类填充表格
                            tb_output_target = DataSettingTableReasoningOutputManager(self.main_window.ui.tableWidgetOutputParameters,
                                                                             self.target_data)
                            tb_output_target.setup_table()

                            self.update_step_labels()
                            self.update_buttons()
                            return  # 直接返回，防止继续执行
                        break

            # 如果没有选中任何 radio button，弹出提示框
            if selected_row is None:
                QMessageBox.warning(self.main_window, "警告", "请先选择一个模型")
                return

        # 检查当前步骤是否为 rBtnTrain 或 rBtnAdjust 正常流程，处理步骤 1 到 3 的跳转
        if self.main_window.ui.rBtnTrain.isChecked() or self.main_window.ui.rBtnAdjust.isChecked():
            if self.current_step == 1:
                # 如果下一步是进入第 3 步，提前收集参数数据

                self.parameter_data = {
                    "parameter_setting": {
                        "task_name": self.main_window.ui.lEditTaskName.text(),
                        "task_type": self.main_window.ui.cmbBoxTaskType.currentText(),
                        "model_type": self.main_window.ui.cmbBoxModelType.currentText(),
                        "submodel": self.get_selected_submodel_data(),  # 获取选中子模型
                        "train": self.main_window.ui.spBoxTrain.value(),
                        "verification": self.main_window.ui.spBoxVerification.value(),
                        "test": self.main_window.ui.spBoxTest.value(),
                        "fill_null": self.main_window.ui.checkBox.isChecked(),
                        "iterations": self.main_window.ui.spBoxIteration.value(),    # 迭代轮次
                        "Tolerance": self.main_window.ui.spBoxTolerance.value(),  # 误差百分比
                        "Performance": self.main_window.ui.lEditPerformance.text(),  # 逆向设计性能指标
                        "assessment": self.get_selected_assessment_data()  # 获取选中评估数据
                    }
                }
                # 判断是否已选择模型
                if not self.parameter_data["parameter_setting"]["submodel"]:
                    # 如果 submodel 为空，则弹出提示框
                    msg_box = QMessageBox()
                    msg_box.setIcon(QMessageBox.Icon.Warning)
                    msg_box.setWindowTitle("提示")
                    msg_box.setText("请选择一个子模型")
                    msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
                    msg_box.exec()
                    return  # 阻止进入下一步

                # 如果已经选择模型，则继续进入下一步
                self.current_step += 1

                print("self.parameter_data",self.parameter_data)
                self.main_window.hyperparameter_data.update(self.parameter_data)
                # 打印 JSON 数据
                print(json.dumps(self.main_window.hyperparameter_data, ensure_ascii=False, indent=4))

                json_file_path = './model.json'
                # 读取现有的 model.json 文件
                with open(json_file_path, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                # 查找 "state" 为 "已创建" 的条目
                created_entry = None
                for entry in data:
                    if entry.get("state") == "已创建":
                        created_entry = entry  # 保存该条目，以便后续使用
                        break  # 找到并保存后退出循环
                # 如果找到了 "已创建" 的条目，将 hyperparameter_data 添加到该条目中
                if created_entry is not None:
                    # 将 hyperparameter_data 作为 "parameter_setting" 的值添加到条目中
                    created_entry["parameter_setting"] = self.main_window.hyperparameter_data

                    # 将修改后的数据写回到 model.json 文件中
                    with open(json_file_path, 'w', encoding='utf-8') as f:
                        json.dump(data, f, ensure_ascii=False, indent=4)

                    print("parameter_setting 已成功添加到 '已创建' 的条目中")
                else:
                    print("未找到 'state' 为 '已创建' 的条目")
                self.update_step_labels()
                self.update_buttons()
            elif self.current_step < 2:
                self.current_step += 1
                self.update_step_labels()
                self.update_buttons()

        # 检查是否选中了 rBtnReasoning，处理步骤 4 到 6 的跳转
        if self.main_window.ui.rBtnReasoning.isChecked():
            if self.current_step == 3:
                self.current_step += 1

                tb_input_features = DataSettingTableReasoningInputManager(self.main_window.ui.tableWidgetSearchRange,
                                                                          self.features_data)
                self.param_ranges = tb_input_features.get_param_ranges()
                print(self.param_ranges)

                self.submodel_data = self.get_ft_data_from_model(self.model_ID, 2)  # 获取 features 数组
                table_manager = SubModelTableManager_res(self.main_window.ui.tableWidgetSubModel_res, self.submodel_data, self)
                table_manager.setup_table()
                self.update_step_labels()
                self.update_buttons()
            elif self.current_step == 3:
                self.current_step += 1
                self.submodel_list = self.get_ft_data_from_model(self.model_ID, 3)
                if self.row is not None and 0 <= self.row < len(self.submodel_list):
                    self.submodel = f"{self.submodel_list[self.row]}.pkl"
                else:
                    raise ValueError("Invalid row index. Please select a valid model.")
                print("获取到的子模型id", self.submodel)
                self.update_step_labels()
                self.update_buttons()

    def get_ft_data_from_model(self, model_id, num):
        models = load_projects()
        for model in models:
            if model.get('ID') == model_id:
                if num == 0:
                    # 找到对应模型，返回其 features 属性
                    return model.get('features', [])
                elif num == 1:
                    return model.get('target', [])
                elif num == 2:
                    return model.get('submodel', [])
                elif num == 3:
                    return model.get('submodel_list', [])
        # 如果未找到匹配的 model_id，返回一个空列表
        return []

    def get_selected_submodel_data(self):
        # 获取 tableWidgetSubModel 中选中行的第二列数据
        selected_submodels = []  # 用于存储选中的子模型名称

        for row in range(self.main_window.ui.tableWidgetSubModel.rowCount()):
            cell_widget = self.main_window.ui.tableWidgetSubModel.cellWidget(row, 0)
            if cell_widget:
                check_box = cell_widget.findChild(QCheckBox)  # 查找复选框
                if check_box and check_box.isChecked():
                    # 获取第二列的数据
                    submodel_item = self.main_window.ui.tableWidgetSubModel.item(row, 1)
                    if submodel_item:
                        selected_submodels.append(submodel_item.text())  # 添加到列表中

        return selected_submodels  # 返回选中的子模型列表


    def get_selected_assessment_data(self):
        # 获取 tableWidgetSubModel 中选中行的第二列数据
        selected_assessment = []  # 用于存储选中的子模型名称

        for row in range(self.main_window.ui.tableWidgetAssess.rowCount()):
            cell_widget = self.main_window.ui.tableWidgetAssess.cellWidget(row, 0)
            if cell_widget:
                check_box = cell_widget.findChild(QCheckBox)
                if check_box and check_box.isChecked():
                    # 获取第二列的数据
                    assessment_item = self.main_window.ui.tableWidgetAssess.item(row, 1)
                    selected_assessment.append(assessment_item.text())  # 添加到列表中
        return selected_assessment

    def analyze(self):
        # 从 parameter_setting 中获取参数


        parameters = self.main_window.hyperparameter_data.get("parameter_setting", {})
        if isinstance(parameters, dict):
            try:
                # 提取 task_type 和 model_type 参数
                task_type = str(parameters.get("task_type")) if parameters.get("task_type") else None
                self.model_type = str(parameters.get("model_type")) if parameters.get("model_type") else None
            except Exception as e:
                print(f"Error extracting parameters: {e}")

        # 获取三个模型的超参数
        hyperparams0 = self.main_window.hyperparameter_data.get("Hyperparameter0", {})
        # 确保 hyperparams 是一个字典
        if isinstance(hyperparams0, dict):
            try:
                # 提取七个参数，并确保 test_size 被正确转换为浮点数
                gb_param1 = float(hyperparams0.get("param1")) if hyperparams0.get("param1") else None  # test_size
                gb_param2 = int(hyperparams0.get("param2")) if hyperparams0.get("param2") else None
                gb_param3 = float(hyperparams0.get("param3")) if hyperparams0.get("param3") else None
                gb_param4 = int(hyperparams0.get("param4")) if hyperparams0.get("param4") else None
                gb_param5 = int(hyperparams0.get("param5")) if hyperparams0.get("param5") else None
                gb_param6 = int(hyperparams0.get("param6")) if hyperparams0.get("param6") else None
                gb_param7 = int(hyperparams0.get("param7")) if hyperparams0.get("param7") else None
                # 将参数传回 hyperparams0
                hyperparams0["param1"] = gb_param1
                hyperparams0["param2"] = gb_param2
                hyperparams0["param3"] = gb_param3
                hyperparams0["param4"] = gb_param4
                hyperparams0["param5"] = gb_param5
                hyperparams0["param6"] = gb_param6
                hyperparams0["param7"] = gb_param7
            except Exception as e:
                print(f"Error extracting parameters: {e}")
        else:
            # 如果 hyperparams 不是字典，给所有参数设置为 None
            hyperparams0 = {
                "param1": None,"param2": None,"param3": None,"param4": None,"param5": None,"param6": None,"param7": None
            }
        print(hyperparams0, "=====================")

        hyperparams1 = self.main_window.hyperparameter_data.get("Hyperparameter1", {})
        if isinstance(hyperparams1, dict):
            try:
                # 提取七个参数，并确保 test_size 被正确转换为浮点数
                svm_param1 = float(hyperparams1.get("param1")) if hyperparams1.get("param1") else None  # test_size
                svm_param2 = int(hyperparams1.get("param2")) if hyperparams1.get("param2") else None
                svm_param3 = float(hyperparams1.get("param3")) if hyperparams1.get("param3") else None
                svm_param4 = int(hyperparams1.get("param4")) if hyperparams1.get("param4") else None
                svm_param5 = int(hyperparams1.get("param5")) if hyperparams1.get("param5") else None
                svm_param6 = int(hyperparams1.get("param6")) if hyperparams1.get("param6") else None
                svm_param7 = int(hyperparams1.get("param7")) if hyperparams1.get("param7") else None
                # 将参数传回 hyperparams1
                hyperparams1["param1"] = svm_param1
                hyperparams1["param2"] = svm_param2
                hyperparams1["param3"] = svm_param3
                hyperparams1["param4"] = svm_param4
                hyperparams1["param5"] = svm_param5
                hyperparams1["param6"] = svm_param6
                hyperparams1["param7"] = svm_param7
            except Exception as e:
                print(f"Error extracting parameters: {e}")
        else:
            # 如果 hyperparams 不是字典，给所有参数设置为 None
            hyperparams1 = {
                "param1": None,"param2": None,"param3": None,"param4": None,"param5": None,"param6": None,"param7": None
            }
        print(hyperparams1, "=====================")
        hyperparams2 = self.main_window.hyperparameter_data.get("Hyperparameter2", {})
        print(hyperparams2, "=====================")

        if self.current_step == 2:
            if self.parameter_data.get("parameter_setting").get("model_type") == "正向搜索":
                self.update_step_labels()
                self.update_buttons()
                self.labelExperimentResult.setStyleSheet(f"background-color: #aaff00")
                # 创建一个models文件夹用于存放模型
                if not os.path.exists("models"):
                    os.makedirs("models")
                    print(f"'models文件夹已创建。")
                model_ID = self.model_ID
                params_gb = {
                    k: v for k, v in {
                        'ID': model_ID,
                        'task_type': task_type,
                        'model_type': self.model_type,
                        'test_size': hyperparams0.get("param1"),
                        'random_state': hyperparams0.get("param2"),
                        'learning_rate': hyperparams0.get("param3"),
                        'max_depth': hyperparams0.get("param4"),
                        'min_samples_leaf': hyperparams0.get("param5"),
                        'min_samples_split': hyperparams0.get("param6"),
                        'n_estimators': hyperparams0.get("param7"),
                    }.items() if v is not None
                }

                params_svr = {
                    k: v for k, v in {
                        'ID': self.model_ID,
                        'task_type': task_type,
                        'model_type': self.model_type,
                        'test_size': hyperparams0.get("param1"),
                        'random_state': hyperparams0.get("param2"),
                        'kernel': hyperparams0.get("param3"),
                        'C': hyperparams0.get("param4"),
                        'epsilon':  hyperparams0.get("param5"),
                        'gamma': hyperparams0.get("param6"),
                        'max_iter':hyperparams0.get("param7"),
                    }.items() if v is not None
                }

                params_rid = {
                    k: v for k, v in {
                        'ID': self.model_ID,
                        'task_type': task_type,
                        'model_type': self.model_type,
                        'test_size': hyperparams0.get("param1"),
                        'random_state': hyperparams0.get("param2"),
                        'alpha': hyperparams0.get("param3"),
                    }.items() if v is not None
                }
                def train_model(model_class, ID,task_type, model_type, excel_path, features, target, **kwargs):
                    # 使用 setdefault 设置默认值
                    model_instance = model_class(ID,task_type, model_type, excel_path, features, target, **kwargs)
                    results = model_instance.train()

                    model_instance.plot_loss_curve()

                    return results

                excel_path = r'C:\Users\13945\Desktop\MLDS铝合金成分设计数据.xlsx'
                features = ['Si/％', 'Mn/％', 'Zn/％', 'Mg/％', 'Cu/％', 'Cr/％', 'Zr/％', 'Ti/％', 'Fe/％', 'Ni/％', 'other']
                target = ['Ultimate tensile strength/MPa']
                tasks = [
                    delayed(train_model)(GradientBoostingModel, **params_gb, excel_path=excel_path,
                                         features=features, target=target),
                    delayed(train_model)(SupportVectorMachineModel, **params_svr, excel_path=excel_path, features=features,
                                         target=target),
                    delayed(train_model)(RidgeRegressionModel, **params_rid, excel_path=excel_path, features=features,
                                         target=target)
                ]

                results = Parallel(n_jobs=-1)(tasks)

                table_manager_pos = ModelTableResultManager(self.main_window)

                result_data = {
                    'model_filename': [],
                    'model_name': [],
                    'model_type': self.model_type,
                    'cv_mean_score': [],
                    'features': features,
                    'target': target,
                }
                # 打印结果
                for result in results:
                    # 获取模型训练的结果
                    model_result = result
                    if model_result is not None:
                        print(json.dumps(model_result, ensure_ascii=False, indent=4))
                        # 将训练结果保存到主窗口对象
                        self.main_window.model_results = model_result
                        print("model_results is", self.main_window.model_results)
                        # 更新表格显示结果
                        table_manager_pos.update_table_pos(model_result)
                        print("------------------------", self.parameter_data.get('parameter_setting', {}).get('submodel', []))
                        # 导入结果数据字典
                        result_data['model_filename'].append(model_result['model_filename'])
                        result_data['model_name'].append(model_result['model_name'])
                        # result_data['model_type'].append(model_result['model_type'])
                        result_data['cv_mean_score'].append(model_result['cv_mean_score'])
                print("result_data is",result_data)
                table_manager_pos.handle_save_button(result_data)

                # 触发更新以显示第一张图片（可选）
                # 添加图片和文本到下拉框
                self.controller = ImageComboBoxController(self.main_window)
                self.controller.add_item('梯度提升树', r'C:\Users\13945\Desktop\picture\plot1.png')
                self.controller.add_item('支持向量机', r'C:\Users\13945\Desktop\picture\plot2.png')
                self.controller.add_item('岭回归', r'C:\Users\13945\Desktop\picture\plot3.png')

                self.controller.connect_signals()
                self.controller.initialize_combobox()  # 初始化下拉框并更新图像
                # controller.connect_signals()
                # 禁用 pBtnAnalysis 按钮
                self.pBtnAnalysis.setEnabled(False)
            else:
                # print(self.parameter_data.get("parameter_setting").get("Performance"))
                self.update_step_labels()
                self.update_buttons()
                self.labelExperimentResult.setStyleSheet(f"background-color: #aaff00")
                self.main_window.ui.pButtonSuspend.clicked.connect(self.request_stop_thread)  # 中止功能


                excel_path = r'C:\Users\13945\Desktop\MLDS铝合金成分设计数据.xlsx'
                performance_columns = ["Ultimate tensile strength/MPa", "Fracture thougness/MPa.m1/2", "Elongation/%"]
                composition_columns = ['Si/％', 'Mn/％', 'Zn/％', 'Mg/％', 'Cu/％', 'Cr/％', 'Zr/％', 'Ti/％', 'Fe/％', 'Ni/％',
                                       'other']
                max_iterations = self.parameter_data.get("parameter_setting").get("iterations")  # 从页面获取迭代次数
                # print(self.parameter_data.get("parameter_setting").get("Tolerance"))  # 从页面获误差百分比
                tolerance = self.parameter_data.get("parameter_setting").get("Tolerance")*0.01
                # tolerance = 0.1
                # print(self.parameter_data.get("parameter_setting").get("Performance"))
                performance_str = self.parameter_data.get("parameter_setting").get("Performance")

                # input_performance = pd.DataFrame([[610, 35, 12]], columns=performance_columns)
                progress_bar_all = self.main_window.ui.pgBarAllTask
                progress_bar_sub = self.main_window.ui.pgBarSubTask
                # 后期要改成从页面获取获取
                self.features_data = composition_columns
                self.target_data = performance_columns

                # 创建预测线程
                self.thread = CompositionPredictorThread(
                    excel_path=excel_path,
                    performance_columns=performance_columns,
                    composition_columns=composition_columns,
                    performance_str=performance_str,
                    max_iterations=max_iterations,
                    tolerance=tolerance
                )

                # # 连接线程信号到进度条和结果处理函数
                self.thread.all_task_progress.connect(progress_bar_all.setValue)
                self.thread.sub_task_progress.connect(progress_bar_sub.setValue)
                self.thread.update_label.connect(self.update_label_round)
                # 新增连接以接收结果
                self.thread.results_ready.connect(self.handle_results)  # 连接结果信号到处理函数
                # 开始线程
                self.thread.start()

                # 禁用 pBtnAnalysis 按钮
                self.pBtnAnalysis.setEnabled(False)


        if self.current_step == 4:
            self.update_step_labels()
            self.update_buttons()
            self.labelExperimentResult.setStyleSheet(f"background-color: #aaff00")
            model_path = f'../Data_Management_page_main/models/{self.model_ID}/{self.submodel}'
            # model_path = f'../Data_Management_page_main/models/20241025145241/20241025145245_gb_model.pkl'
            print(model_path)
            param_ranges = {
                'Si/％': (0.06, 0.06, 1),
                'Mn/％': (0.5, 0.5, 1),
                'Zn/％': (5.8, 5.8, 1),
                'Mg/％': (2.17, 2.17, 1),
                'Cu/％': (1.7, 1.7, 1),
                'Cr/％': (0.10, 100.00, 0.01),
                'Zr/％': (0, 0, 1),
                'Ti/％': (0.019, 0.019, 1),
                'Fe/％': (0.086, 0.086, 1),
                'Ni/％': (0.025, 0.025, 1),
                'other': (0, 0, 1),
            }
            target_min = 0
            target_max = 10000
            # 获取进度条控件
            progress_bar = self.main_window.ui.pgBarSubTask_res
            inference = ModelInference(model_path, param_ranges, target_min, target_max, progress_bar)
            res = inference.run_inference()
            # print(res)
            # targets = ['拉伸强度']
            # attributes = ['Si/％', 'Mn/％', 'Zn/％', 'Mg/％', 'Cu/％', 'Cr/％', 'Zr/％', 'Ti/％', 'Fe/％', 'Ni/％', 'other']
            # print("self.target_data", self.target_data)
            # print("self.features_data is", self.features_data)
            table_manager = ModelTableResultManager_res(self.main_window)
            table_manager.setup_table(self.target_data, self.features_data, res)

    #  添加每轮动态展示
    def update_label_round(self, current_iteration, total_iterations, count_within_tolerance):
        self.main_window.ui.labelRound.setText(
            f"第{current_iteration}轮/共{total_iterations}轮 满足条件数据集有{count_within_tolerance}个")

    # 添加请求停止线程的函数
    def request_stop_thread(self):
        if hasattr(self, 'thread') and self.thread.isRunning():
            self.thread.stop_requested = True  # 设置线程中的停止标志

    # 定义槽函数来处理结果
    def handle_results(self, results, tolerance):
        print("接收到的结果:", results)
        self.results = results  # 将结果存储在实例属性中
        table_manager_rev = ModelTableResultManager(self.main_window)
        table_manager_rev.update_table_rev(self.features_data, self.target_data, self.results, tolerance)

# 数据界面绘制直方图类
class HistogramWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        layout = QVBoxLayout(self)
        layout.addWidget(self.canvas)
        self.setLayout(layout)

    def plot_histogram(self, data):
        plt.rcParams['font.sans-serif'] = ['SimHei']
        plt.rcParams['axes.unicode_minus'] = False
        self.figure.clear()
        ax = self.figure.add_subplot(111)

        # 绘制直方图
        try:
            # 绘制直方图
            if pd.api.types.is_numeric_dtype(data):
                ax.hist(data, bins=20, edgecolor='black')
                ax.set_title(f'{data.name} 直方图')
                ax.set_xlabel(f'{data.name}')
                ax.set_ylabel('Frequency')
            else:
                data.value_counts().plot(kind='bar', ax=ax)
                ax.set_title(f'{data.name} 频数图')
                ax.set_xlabel(f'{data.name}')
                ax.set_ylabel(f'Count({data.name})')

            self.canvas.draw()

        except Exception as e:
            # 弹出错误对话框
            QMessageBox.critical(self, "绘图错误", "绘图失败，请更改数据类型重试。错误信息: " + str(e))

    def clear_plot(self):
        self.figure.clear()
        self.canvas.draw()

# 数据界面界面三
class CustomPage3(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.histogram = HistogramWidget(parent=self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.histogram)
        self.setLayout(layout)

    def plot_histogram(self, data):
        self.histogram.plot_histogram(data)

    def clear_plot(self):
        self.histogram.clear_plot()

# 数据界面界面二
class CustomPage2(QWidget):
    visualize_signal = pyqtSignal(str)  # 定义信号

    def __init__(self, parent=None):
        super().__init__(parent)

        # 创建主布局
        self.main_layout = QVBoxLayout(self)

        # 添加标题行
        self.add_header()

        # 创建一个ScrollArea以处理大量数据
        self.scroll_area = QScrollArea(self)
        self.scroll_area.setWidgetResizable(True)
        self.scroll_content = QWidget()
        self.scroll_layout = QVBoxLayout(self.scroll_content)
        self.scroll_area.setWidget(self.scroll_content)


        # 将ScrollArea添加到主布局中
        self.main_layout.addWidget(self.scroll_area)
        self.setLayout(self.main_layout)  # 确保设置了布局

    def add_header(self):
        header_layout = QHBoxLayout()
        header_labels = ['属性名', '数据类型', '缺失数目', '数据', '操作']

        for text in header_labels:
            label = QLabel(text)
            label.setStyleSheet("border: 1px solid red;")  # 设置边框以调试
            label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)  # 设置为伸缩策略
            header_layout.addWidget(label)

        self.main_layout.addLayout(header_layout)

    def add_row(self, attr_name, data_type, missing_count, data_summary):
        row_widget = QWidget()
        row_layout = QHBoxLayout(row_widget)

        # 创建并添加每一列
        row_layout.addWidget(QLabel(attr_name))
        row_layout.addWidget(QLabel(data_type))
        row_layout.addWidget(QLabel(str(missing_count)))
        row_layout.addWidget(QLabel(data_summary))

        # 操作按钮
        visualize_button = QPushButton('可视化')
        # 为按钮连接槽函数
        visualize_button.clicked.connect(lambda: self.visualize_signal.emit(attr_name))
        row_layout.addWidget(visualize_button)

        # 将行添加到滚动区域的布局中
        self.scroll_layout.addWidget(row_widget)

    def clear_rows(self):
        # 清除滚动区域的所有行
        while self.scroll_layout.count():
            child = self.scroll_layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()



class MenuItem(QWidget):
    def __init__(self, text, icon):
        super(MenuItem, self).__init__()
        lay = QHBoxLayout()
        labelIcon = QLabel()
        labelIcon.setPixmap(QtGui.QPixmap(icon))
        labelIcon.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
        labelIcon.setContentsMargins(5, 0, 5, 0)
        lay.addWidget(labelIcon)
        labelItemName = QLabel(text)
        labelItemName.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
        labelItemName.setStyleSheet('''
            font-family: "Microsoft YaHei";
            font-weight: 400;
            font-size: 14px;
            color: #dcefff;
            border: 1px solid rgba(0, 0, 0, 0);
        ''')
        lay.addWidget(labelItemName)
        labelIndicator = QLabel()
        labelIndicator.setPixmap(QtGui.QPixmap(":/右箭头.png"))
        labelIndicator.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
        lay.addWidget(labelIndicator)
        lay.setSpacing(0)
        self.setLayout(lay)
        self.icon = labelIcon

    def setIcon(self, icon):
        self.icon.setPixmap(QtGui.QPixmap(icon))


class MenuManagerParameterSettings:
    def __init__(self, list_widget_menu, scroll_area, widgets):
        self.list_widget_menu = list_widget_menu
        self.scroll_area = scroll_area
        self.widgets = widgets  # 传入右侧的各个 QWidget 对象，分别对应每个设置项
        self.menu_items = []

        # 初始化菜单项
        self.init_menu_items()

        # 连接菜单项选择改变的信号
        self.list_widget_menu.currentRowChanged.connect(self.scroll_to_setting)

    def init_menu_items(self):
        # 添加菜单项到 listWidgetMenu 中
        self.menu_items.append(self.add_menu_item('模型设置', ":/模型设置.png"))
        self.menu_items.append(self.add_menu_item('参数设置', ":/参数设置.png"))
        self.menu_items.append(self.add_menu_item('模型训练', ":/模型训练.png"))

        # 设置初始状态为第一个菜单项
        self.list_widget_menu.setCurrentRow(0)

    def add_menu_item(self, text, icon):
        item = QListWidgetItem()
        self.list_widget_menu.addItem(item)
        item.setSizeHint(QtCore.QSize(100, 60))
        menu_item = MenuItem(text, icon)
        self.list_widget_menu.setItemWidget(item, menu_item)
        return menu_item

    def reset_menu_icons(self):
        # 重置所有菜单项的图标
        self.menu_items[0].setIcon(":/模型设置.png")
        self.menu_items[1].setIcon(":/参数设置.png")
        self.menu_items[2].setIcon(":/模型训练.png")

    def scroll_to_setting(self, row):
        # 根据选中的行，滚动到相应的设置项
        if row == 0:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['model_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[0].setIcon(":/基本设置-选中.png")
        elif row == 1:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['data_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[1].setIcon(":/路径设置-选中.png")
        elif row == 2:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['train_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[2].setIcon(":/升级设置-选中.png")
class MenuManagerExperimentalResult:
    def __init__(self, list_widget_menu, scroll_area, widgets):
        self.list_widget_menu = list_widget_menu
        self.scroll_area = scroll_area
        self.widgets = widgets  # 传入右侧的各个 QWidget 对象，分别对应每个设置项
        self.menu_items = []

        # 初始化菜单项
        self.init_menu_items()

        # 连接菜单项选择改变的信号
        self.list_widget_menu.currentRowChanged.connect(self.scroll_to_setting)

    def init_menu_items(self):
        # 添加菜单项到 listWidgetMenu 中
        self.menu_items.append(self.add_menu_item('训练进度', ":/训练进度.png"))
        self.menu_items.append(self.add_menu_item('测试集结论', ":/测试集结论.png"))
        self.menu_items.append(self.add_menu_item('损失曲线', ":/损失曲线.png"))

        # 设置初始状态为第一个菜单项
        self.list_widget_menu.setCurrentRow(0)

    def add_menu_item(self, text, icon):
        item = QListWidgetItem()
        self.list_widget_menu.addItem(item)
        item.setSizeHint(QtCore.QSize(100, 60))
        menu_item = MenuItem(text, icon)
        self.list_widget_menu.setItemWidget(item, menu_item)
        return menu_item

    def reset_menu_icons(self):
        # 重置所有菜单项的图标
        self.menu_items[0].setIcon(":/训练进度.png")
        self.menu_items[1].setIcon(":/测试集结论.png")
        self.menu_items[2].setIcon(":/损失曲线.png")

    def scroll_to_setting(self, row):
        # 根据选中的行，滚动到相应的设置项
        if row == 0:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['Train_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[0].setIcon(":/训练进度-选中.png")
        elif row == 1:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['Conclusion_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[1].setIcon(":/测试集结论-选中.png")
        elif row == 2:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['Loss_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[2].setIcon(":/损失曲线-选中.png")

class MenuManagerExperimentalResult_res:
    def __init__(self, list_widget_menu, scroll_area, widgets):
        self.list_widget_menu = list_widget_menu
        self.scroll_area = scroll_area
        self.widgets = widgets  # 传入右侧的各个 QWidget 对象，分别对应每个设置项
        self.menu_items = []

        # 初始化菜单项
        self.init_menu_items()

        # 连接菜单项选择改变的信号
        self.list_widget_menu.currentRowChanged.connect(self.scroll_to_setting)

    def init_menu_items(self):
        # 添加菜单项到 listWidgetMenu 中
        self.menu_items.append(self.add_menu_item('推理进度', ":/推理进度.png"))
        self.menu_items.append(self.add_menu_item('最优目标搜索结论', ":/最优目标搜索结论.png"))

        # 设置初始状态为第一个菜单项
        self.list_widget_menu.setCurrentRow(0)

    def add_menu_item(self, text, icon):
        item = QListWidgetItem()
        self.list_widget_menu.addItem(item)
        item.setSizeHint(QtCore.QSize(100, 60))
        menu_item = MenuItem(text, icon)
        self.list_widget_menu.setItemWidget(item, menu_item)
        return menu_item

    def reset_menu_icons(self):
        # 重置所有菜单项的图标
        self.menu_items[0].setIcon(":/推理进度.png")
        self.menu_items[1].setIcon(":/最优目标搜索结论.png")

    def scroll_to_setting(self, row):
        # 根据选中的行，滚动到相应的设置项
        if row == 0:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['Inference_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[0].setIcon(":/推理进度-选中.png")
        elif row == 1:
            self.scroll_area.verticalScrollBar().setSliderPosition(self.widgets['Conclusion_setting'].pos().y())
            self.reset_menu_icons()
            self.menu_items[1].setIcon(":/最优目标搜索结论-选中.png")

class DataManagerApp(QWidget):
    def __init__(self):
        super().__init__()
        # 存储超参数 JSON 数据
        self.hyperparameter_data = {"Hyperparameter0": "", "Hyperparameter1": "", "Hyperparameter2": ""}


        self.ui = Ui_DataManagerwidget()
        self.ui.setupUi(self)

        # 创建菜单
        self.createMenus()

        # 绑定按钮点击事件到相应的槽函数
        self.ui.toolButtonP1.clicked.connect(lambda: self.select_page(self.ui.page1))
        self.ui.toolButtonP2.clicked.connect(lambda: self.select_page(self.ui.page2))
        self.ui.toolButtonP3.clicked.connect(lambda: self.select_page(self.ui.page3))

        # 隐藏窗体顶部
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        # 设置鼠标追踪
        self.setMouseTracking(True)
        # 设置拖动相关变量
        self.is_maximized = False
        self.initDrag()

        # 记录初始位置和大小
        self.initial_geometry = self.geometry()

        # 连接窗口控制按钮
        self.ui.pBtnClose.clicked.connect(lambda: [self.handle_close_event(), self.close()])
        # self.ui.pBtnClose.clicked.connect(self.close)
        self.ui.pBtnBig.clicked.connect(self.toggle_maximize)
        self.ui.pBtnSmall.clicked.connect(self.showMinimized)

        # 加载模型页面第一页模型管理表格==================================================================
        # 实例化 ModelTableManager
        self.model_table_manager = ModelTableProjectManager(self.ui.tableWidget_model)
        self.model_table_manager.setup_modelTable()
        self.projects = load_projects()
        self.filtered_projects = self.projects  # 保持当前的过滤结果
        self.model_table_manager.load_model_table(self.filtered_projects)
        # 记录被选中的行
        self.selected_row = None
        # QStackedWidget按钮控制页面跳转
        self.step_manager = ModelStepManager(self)
        # 加载模型页面第二页数据设置表格（推理）==================================================================


        # 逆向设置按钮隐藏和显示==================================================================
        self.cmbBoxModelType = self.findChild(QComboBox, "cmbBoxModelType")
        self.labelPerformance = self.findChild(QLabel, "labelPerformance")
        self.lEditPerformance = self.findChild(QLineEdit, "lEditPerformance")
        self.labelTolerance = self.findChild(QLabel, "labelTolerance")
        self.spBoxTolerance = self.findChild(QSpinBox, "spBoxTolerance")
        self.tableWidgetSubModel = self.findChild(QTableWidget, "tableWidgetSubModel")
        self.tableWidgetAssess = self.findChild(QTableWidget, "tableWidgetAssess")
        self.labelAssess = self.findChild(QLabel, "labelAssess")

        self.submodel_table_manager = SubModelTableManager(self.tableWidgetSubModel, self)
        self.submodel_table_manager.setup_modelTable([100, 500, 300])

        self.assess_table_manager = SubModelTableManager(self.ui.tableWidgetAssess, self)
        self.assess_table_manager.setup_modelTable([100, 700])
        # 连接信号和槽
        self.cmbBoxModelType.currentTextChanged.connect(self.update_visibility)

        # 初始化控件的可见性和表格内容
        self.update_visibility(self.cmbBoxModelType.currentText())
        # 加载数据并显示在表格中
        # subModel = [
        #     {"name": "梯度提升树"},
        #     {"name": "支持向量机"},
        #     {"name": "岭回归"},
        # ]  # 存储从JSON中获取的数据
        # self.submodel_table_manager.load_model_table(subModel)
        # 加载模型页面第三页参数设置表格==================================================================
        # self.assess_table_manager = SubModelTableManager(self.ui.tableWidgetAssess, self)
        # self.assess_table_manager.setup_modelTable([100, 700])
        # # 加载数据并显示在表格中
        # assess = [
        #     {"name": "MSE"},
        #     {"name": "R²"},
        # ]  # 存储从JSON中获取的数据
        # self.assess_table_manager.load_model_table(assess)
        # ====================================================================================
        # 训练左侧菜单项与右侧设置项的对应关系（参数设置)
        WidgetsParameterSetting = {
            'model_setting': self.ui.widgetModelSettingTitle,
            'data_setting': self.ui.widgetDataSettingTitle,
            'train_setting': self.ui.widgetTrainParametersTitle,
        }
        # 创建参数设置页菜单管理器
        self.menu_manager_setting = MenuManagerParameterSettings(self.ui.listWidgetMenu, self.ui.scrollArea, WidgetsParameterSetting)
        # 训练左侧菜单项与右侧设置项的对应关系(实验结果页面训练)
        WidgetsExperimentalResult = {
            'Train_setting': self.ui.widgetTrainProgressTitle,
            'Conclusion_setting': self.ui.widgetConclusionTitle,
            'Loss_setting': self.ui.widgetLossTitle,
        }
        # 创建参数设置页菜单管理器
        self.menu_manager_result = MenuManagerExperimentalResult(self.ui.listWidgetMenu_2, self.ui.scrollArea_2, WidgetsExperimentalResult)
        # 推理左侧菜单项与右侧设置项的对应关系(实验结果页面推理)
        WidgetsExperimentalResult_res = {
            'Inference_setting': self.ui.widgetInferenceProgressTitle,
            'Conclusion_setting': self.ui.widgetConclusionTitle_res,
        }
        # 创建参数设置页菜单管理器
        self.menu_manager_result_res = MenuManagerExperimentalResult_res(self.ui.listWidgetMenu_3, self.ui.scrollArea_2,
                                                                 WidgetsExperimentalResult_res)
        # 获取控件做隐藏按钮操作==================================================================
        self.rBtnTrain = self.findChild(QRadioButton, "rBtnTrain")
        self.rBtnAdjust = self.findChild(QRadioButton, "rBtnAdjust")
        self.rBtnReasoning = self.findChild(QRadioButton, "rBtnReasoning")

        self.pBtnTrain = self.findChild(QPushButton, "pBtnTrain")
        self.pBtnAdjust = self.findChild(QPushButton, "pBtnAdjust")
        self.pBtnReasoning = self.findChild(QPushButton, "pBtnReasoning")
        self.pBtnLog = self.findChild(QPushButton, "pBtnLog")
        self.pBtnDelete = self.findChild(QPushButton, "pBtnDelete")

        self.widgetModelAnalysisBtn = self.findChild(QWidget, "widgetModelAnalysisBtn")

        # 连接信号和槽
        self.rBtnTrain.toggled.connect(self.update_buttons)
        self.rBtnAdjust.toggled.connect(self.update_buttons)
        self.rBtnReasoning.toggled.connect(self.update_buttons)
        # 连接信号和槽
        self.pBtnTrain.clicked.connect(self.show_train_dialog)



        self.update_buttons()  # 初始化时更新按钮显示状态
        # QStackedWidget按钮控制页面跳转==================================================================
        self.stacked_widget = self.findChild(QStackedWidget, "stackedWidget_2")
        self.pBtnData = self.findChild(QPushButton, "pBtnData")
        self.pBtnModel = self.findChild(QPushButton, "pBtnModel")
        # 默认显示第一个数据页面
        self.stacked_widget.setCurrentIndex(0)
        # 连接按钮的点击事件
        self.pBtnData.clicked.connect(self.show_data_page)
        self.pBtnModel.clicked.connect(self.show_model_page)

        # 初始化页面和按钮状态
        self.setup_buttons()

        # 默认选中第一个按钮
        self.select_button(self.ui.toolButtonP1)

        # 创建并添加自定义页面 CustomPage2 到 page2 中
        self.custom_page2 = CustomPage2()
        self.ui.page2.layout().addWidget(self.custom_page2)  # 使用现有的布局
        self.custom_page2.visualize_signal.connect(self.visualize_column)

        # # 创建 CustomPage3 实例
        self.custom_page3 = CustomPage3()
        # 清除现有的布局内容
        while self.ui.page3.layout().count():
            child = self.ui.page3.layout().takeAt(0)
            if child.widget():
                child.widget().deleteLater()

        # 添加 CustomPage3 到 page3
        self.ui.page3.layout().addWidget(self.custom_page3)
        # self.ui.stackedWidget.addWidget(self.custom_page3)   #新增一个页面
        # 初始化uploaded_data
        self.uploaded_data = None  # 用于存储上传的Excel数据
        # 连接 stackedWidget 页面切换信号
        self.ui.stackedWidget.currentChanged.connect(self.on_page_changed)
        # 初始化表头右键菜单功能
        self.init_table_widget_context_menu()

        self.header_labels = {}  # 用于存储表头标签的持久化字典

        self.dialog_open = False
    # 获取parameter_data==================================================================

    # def pr_parameter_data(self):
    #     parameter_data = self.step_manager.get_parameter_data()
    #     print(json.dumps(parameter_data, ensure_ascii=False, indent=4))

    # 正向搜索下拉框展示和隐藏
    def update_visibility(self, text):
        if text == "正向搜索":
            self.labelPerformance.hide()
            self.lEditPerformance.hide()
            self.labelTolerance.hide()
            self.spBoxTolerance.hide()
            subModel = [
                {"name": "梯度提升树"},
                {"name": "支持向量机"},
                {"name": "岭回归"},
            ]
            assess = [
                {"name": "MSE"},
                {"name": "R²"},
            ]
            self.labelAssess.show()
            self.tableWidgetAssess.show()
        else:
            self.labelPerformance.show()
            self.lEditPerformance.show()
            self.labelTolerance.show()
            self.spBoxTolerance.show()
            subModel = [{"name": "随机森林"}]
            assess = []
            self.tableWidgetAssess.hide()
            self.labelAssess.hide()
            self.tableWidgetAssess.hide()
        self.assess_table_manager.load_model_table(assess)
        self.submodel_table_manager.load_model_table(subModel)
    def show_train_dialog(self):
        dialog = TrainModelDialog(self)
        if dialog.exec() == QDialog.DialogCode.Accepted:
            # 获取输入内容
            name = dialog.name_input.text()
            type_ = dialog.type_input.text()
            submodel = dialog.submodel_input.text()

            # 获取当前时间
            created_time = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
            ID = datetime.now().strftime("%Y%m%d%H%M%S")
            # 获取导入的Excel数据行数
            train_set = f"{self.uploaded_data.shape[0]}条" if self.uploaded_data is not None else ""

            # 创建新的模型数据条目
            new_entry = {
                "ID": ID,
                "name": name,
                "type": type_,
                "created_time": created_time,
                "state": "已创建",
                "submodel": submodel,
                "submodel_list": "",
                "train_set": train_set,
                "Validation_set": ""
            }

            # 将新条目加入项目列表
            self.projects.append(new_entry)
            # 保存到JSON文件
            save_projects(self.projects)
            # 刷新表格
            self.model_table_manager.load_model_table(self.filtered_projects)

            # 禁用 pBtnTrain 按钮
            self.pBtnTrain.setEnabled(False)
            self.update_buttons()  # 更新按钮状态
    # =================================================
    def show_data_page(self):
        self.stacked_widget.setCurrentIndex(0)
    def show_model_page(self):
        self.stacked_widget.setCurrentIndex(1)
    # =================================================
    def update_buttons(self):
        # 根据哪个 QRadioButton 被选中来更新按钮的显示状态
        if self.rBtnTrain.isChecked():
            self.show_buttons([self.pBtnTrain])

            self.filtered_projects = [project for project in self.projects if project.get("state") == "已创建"]
            self.model_table_manager.load_model_table(self.filtered_projects)  # 加载筛选后的数据

            # 禁用 tableWidget_model 中的所有 RadioButton
            self.set_radio_buttons_enabled(False)
            # 清除所有的选中状态
            self.clear_all_radio_buttons()

        elif self.rBtnAdjust.isChecked() or self.rBtnReasoning.isChecked():
            if self.rBtnAdjust.isChecked():
                self.show_buttons([self.pBtnAdjust, self.pBtnLog, self.pBtnDelete])
            else:
                self.show_buttons([self.pBtnReasoning, self.pBtnLog, self.pBtnDelete])
            self.filtered_projects = [project for project in self.projects if project.get("state") == "已完成"]
            self.model_table_manager.load_model_table(self.filtered_projects)  # 加载筛选后的数据

            # 启用 tableWidget_model 中的所有 RadioButton
            self.set_radio_buttons_enabled(True)
        else:
            self.show_buttons([])  # 如果没有任何 radio button 选中，隐藏所有按钮

    def clear_all_radio_buttons(self):
        for row in range(self.ui.tableWidget_model.rowCount()):
            radio_button = self.ui.tableWidget_model.cellWidget(row, 0)
            if radio_button and isinstance(radio_button, QtWidgets.QRadioButton):
                if radio_button.isChecked():
                    radio_button.setChecked(False)
        QApplication.processEvents()  # 强制处理挂起的事件

    def set_radio_buttons_enabled(self, enabled):
        # 启用或禁用 tableWidget_model 中的所有 RadioButton
        for row in range(self.ui.tableWidget_model.rowCount()):
            radio_button = self.ui.tableWidget_model.cellWidget(row, 0)
            if radio_button:
                radio_button.setEnabled(enabled)

    def show_buttons(self, buttons):
        # 隐藏所有按钮
        for btn in [self.pBtnTrain, self.pBtnAdjust, self.pBtnReasoning, self.pBtnLog, self.pBtnDelete]:
            btn.setVisible(False)
        # 仅显示指定的按钮
        for btn in buttons:
            btn.setVisible(True)
    # =================================================
    def init_table_widget_context_menu(self):
        # 获取 tableWidget（在 page1 中）
        table_widget = self.ui.page1.findChild(QTableWidget, "tableWidget")

        if table_widget:
            # 设置行和列的右键菜单
            table_widget.horizontalHeader().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
            table_widget.verticalHeader().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)

            # 绑定右键点击事件到槽函数
            table_widget.horizontalHeader().customContextMenuRequested.connect(self.show_horizontal_header_menu)
            table_widget.verticalHeader().customContextMenuRequested.connect(self.show_vertical_header_menu)

    def show_horizontal_header_menu(self, position):
        # 将位置转换为列索引
        column_index = self.ui.tableWidget.horizontalHeader().logicalIndexAt(position)

        # 获取对应列的表头文本
        header_item = self.ui.tableWidget.horizontalHeaderItem(column_index)
        header_text = header_item.text() if header_item else "Unknown"

        # 在水平表头位置显示右键菜单
        header_menu = QMenu(self)
        header_menu.addAction("水平表头操作1", lambda: self.horizontal_header_action1(position, header_text))
        header_menu.addAction("水平表头操作2", self.horizontal_header_action2)
        header_menu.addAction("水平表头操作3", self.horizontal_header_action3)
        header_menu.exec(self.ui.tableWidget.horizontalHeader().mapToGlobal(position))

    def show_vertical_header_menu(self, position):
        # 在垂直表头位置显示右键菜单
        header_menu = QMenu(self)
        header_menu.addAction("垂直表头操作1", self.vertical_header_action1)
        header_menu.addAction("垂直表头操作2", self.vertical_header_action2)
        header_menu.exec(self.ui.tableWidget.verticalHeader().mapToGlobal(position))

    def horizontal_header_action1(self, position, header_text):
        try:
            header = self.ui.tableWidget.horizontalHeader()
            column_index = header.logicalIndexAt(position)
            print("column_index is", column_index)
            if column_index != -1:
                self.show_type_setting_window(column_index, header_text)

                # selected_type_str = self.dialog.get_selected_type()
                # if selected_type_str:
                #     selected_type = type_mapping.get(selected_type_str, str)
                    # 现在可以使用 selected_type 作为 Python 类型进行操作
        except Exception as e:
            print(f"Error in horizontal_header_action1: {e}")
            QMessageBox.critical(self, "错误", f"发生错误: {e}")

    def show_type_setting_window(self, column_index, header_text):
        if self.dialog_open:
            return

        self.set_controls_enabled(False)  # 禁用主窗口控件
        self.dialog_open = True

        dialog = TypeSettingWindow(column_index, header_text, self.ui.tableWidget, self)
        dialog.setModal(True)  # 设置对话框为模态

        try:
            # 显示对话框并等待用户操作
            if dialog.exec() == QDialog.DialogCode.Accepted:
                # 获取用户选择的类型
                new_type = dialog.get_selected_type()
                # 获取当前列名（去掉标签）
                column_name = self.ui.tableWidget.horizontalHeaderItem(column_index).text().split('(')[0].strip()
                # 更新表头标签的持久化数据结构
                new_header_label = f"{column_name} ({new_type.capitalize()})"
                self.header_labels[column_name] = new_header_label
                try:
                    if new_type == "float":
                        self.uploaded_data[column_name] = self.uploaded_data[column_name].astype(float)
                    elif new_type == "object":
                        self.uploaded_data[column_name] = self.uploaded_data[column_name].astype(str)
                except Exception as e:
                    print(f"Error during type conversion: {e}")  # 捕获并打印异常
                    QMessageBox.critical(self, "错误", f"类型转换时发生错误: {e}")
        finally:
            # 确保对话框关闭后恢复主窗口控件的启用状态
            self.dialog_open = False
            self.set_controls_enabled(True)
    def set_controls_enabled(self, enabled: bool):
        # 根据需要禁用和启用主窗口中的控件
        self.ui.tableWidget.setEnabled(enabled)
        self.ui.widgetToolboxTop.setEnabled(enabled)
        self.ui.widgetDataModel.setEnabled(enabled)
        self.ui.widgetToolboxLeft.setEnabled(enabled)


    def horizontal_header_action2(self):
        print("水平表头操作2触发")

    def horizontal_header_action3(self):
        print("水平表头操作3触发")

    def vertical_header_action1(self):
        print("垂直表头操作1触发")

    def vertical_header_action2(self):
        print("垂直表头操作2触发")
    def select_page(self, page):
        if page == self.ui.page1:
            self.show_page1()
        elif page == self.ui.page2:
            self.show_page2()
        elif page == self.ui.page3:
            self.show_page3()

    # 隐藏自带框
    def initDrag(self):
        # 设置鼠标跟踪判断扳机默认值
        self.moveDrag = False

    def mousePressEvent(self, event):
        if (
                event.button() == Qt.MouseButton.LeftButton) and not self.toolButtonsUnderMouse() and self.ui.widgetTop.underMouse():
            if self.is_maximized:
                self.toggle_maximize()
                self.moveDragPosition = event.globalPosition().toPoint() - self.geometry().topLeft()
            else:
                self.moveDragPosition = event.globalPosition().toPoint() - self.pos()
            self.moveDrag = True
            event.accept()

    def mouseMoveEvent(self, event):
        if self.moveDrag:
            self.move(event.globalPosition().toPoint() - self.moveDragPosition)
            event.accept()

    def mouseReleaseEvent(self, event):
        self.moveDrag = False

    def mouseDoubleClickEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton and self.ui.widgetTop.underMouse():
            self.toggle_maximize()

    def toolButtonsUnderMouse(self):
        return self.ui.pBtnClose.underMouse() or self.ui.pBtnBig.underMouse() or self.ui.pBtnSmall.underMouse()

    def toggle_maximize(self):
        if self.is_maximized:
            self.setGeometry(self.initial_geometry)
            self.is_maximized = False
        else:
            self.initial_geometry = self.geometry()
            self.showMaximized()
            self.is_maximized = True


    # 菜单栏相关逻辑
    def createMenus(self):
        # 创建文件菜单
        self.fileMenu = QtWidgets.QMenu(self)
        # 创建“导入数据”菜单项，并为其添加二级菜单
        self.importDataMenu = QtWidgets.QMenu("导入数据", self)
        self.appendDataAction = self.importDataMenu.addAction("末尾追加")
        self.overwriteDataAction = self.importDataMenu.addAction("覆盖旧数据")

        # 将导入数据的二级菜单添加到文件菜单
        self.fileMenu.addMenu(self.importDataMenu)

        self.goBackAction = self.fileMenu.addAction("返回项目管理页")
        self.exitAction = self.fileMenu.addAction("退出")

        # 连接文件菜单项到槽函数
        self.appendDataAction.triggered.connect(self.append_data)
        self.overwriteDataAction.triggered.connect(self.overwrite_data)
        self.goBackAction.triggered.connect(self.goback)
        self.exitAction.triggered.connect(self.exitApp)

        # 将文件菜单连接到文件按钮
        self.ui.pBtnFile.setMenu(self.fileMenu)


        # 创建文件菜单
        self.editMenu = QtWidgets.QMenu(self)
        self.backAction = self.editMenu.addAction("上一步")
        self.nextAction = self.editMenu.addAction("下一步")

        self.backAction.triggered.connect(self.back)
        self.nextAction.triggered.connect(self.next)

        self.ui.pBtnEdit.setMenu(self.editMenu)

        self.windowsMenu = QtWidgets.QMenu(self)
        self.windowsAction = self.windowsMenu.addAction("窗口")
        self.windowsAction.triggered.connect(self.windows)
        self.ui.pBtnWindow.setMenu(self.windowsMenu)

        self.helpMenu = QtWidgets.QMenu(self)
        self.aboutAction = self.helpMenu.addAction("使用帮助")
        self.aboutAction.triggered.connect(self.about)
        self.ui.pBtnHelp.setMenu(self.helpMenu)
        self.uploaded_data = None  # 用于存储上传的Excel数据



        # 其他初始化逻辑
        self.ui.listWidgetMenu.setFixedWidth(180)
        self.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
        self.ui.frame.setFrameStyle(QFrame.Shape.Panel)
    # 需要定义处理子菜单项点击的槽函数
    def append_data(self):
        # 实现末尾追加数据的逻辑
        pass

    def goback(self):
        pass

    def back(self):
        pass

    def next(self):
        pass

    def exitApp(self):
        QtWidgets.QApplication.quit()

    def edit(self):
        QtWidgets.QMessageBox.information(self, "编辑", "编辑信息")

    def windows(self):
        QtWidgets.QMessageBox.information(self, "窗口", "窗口信息")

    def about(self):
        QtWidgets.QMessageBox.information(self, "关于", "关于信息")


    # 槽函数用于切换页面
    def setup_buttons(self):
        # 绑定按钮点击事件到相应的槽函数
        self.ui.toolButtonP1.clicked.connect(lambda: self.select_button(self.ui.toolButtonP1))
        self.ui.toolButtonP2.clicked.connect(lambda: self.select_button(self.ui.toolButtonP2))
        self.ui.toolButtonP3.clicked.connect(lambda: self.select_button(self.ui.toolButtonP3))

    def select_button(self, button):
        # 取消所有按钮的选中状态
        self.ui.toolButtonP1.setProperty('selected', False)
        self.ui.toolButtonP2.setProperty('selected', False)
        self.ui.toolButtonP3.setProperty('selected', False)

        # 选中当前按钮
        button.setProperty('selected', True)

        # 刷新所有按钮的样式表
        self.ui.toolButtonP1.style().unpolish(self.ui.toolButtonP1)
        self.ui.toolButtonP1.style().polish(self.ui.toolButtonP1)
        self.ui.toolButtonP2.style().unpolish(self.ui.toolButtonP2)
        self.ui.toolButtonP2.style().polish(self.ui.toolButtonP2)
        self.ui.toolButtonP3.style().unpolish(self.ui.toolButtonP3)
        self.ui.toolButtonP3.style().polish(self.ui.toolButtonP3)

    def restore_header_labels(self):
        # 获取表头
        header = self.ui.tableWidget.horizontalHeader()
        model = self.ui.tableWidget.model()  # 获取模型对象

        for column_name, label in self.header_labels.items():
            # 遍历所有列来找到匹配的列名
            for column_index in range(header.count()):
                header_item = model.headerData(column_index, Qt.Orientation.Horizontal)
                if header_item and column_name in header_item:
                    # 更新表头数据
                    model.setHeaderData(column_index, Qt.Orientation.Horizontal, label)
                    print(f"Updated column '{column_name}' to '{label}'")
                    break

    def show_page1(self):
        try:

            self.ui.stackedWidget.setCurrentWidget(self.ui.page1)
            if self.uploaded_data is not None:
                self.populate_table()
                self.restore_header_labels()
        except Exception as e:
            print(f"Error in show_page1: {e}")
            QMessageBox.critical(self, "错误", f"切换到 Page 1 时发生错误: {e}")


    def show_page2(self):
        if self.ui.stackedWidget.currentWidget() != self.ui.page2:
            self.ui.stackedWidget.setCurrentWidget(self.ui.page2)
            print("Switched to page2")
            self.populate_custom_page2()  # 确保在切换页面时填充数据


    def overwrite_data(self):
        filePath, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xls *.xlsx);;All Files (*)")

        if filePath:
            try:
                df = pd.read_excel(filePath)
                self.uploaded_data = df
                print(f"Data loaded: {self.uploaded_data.shape[0]} rows, {self.uploaded_data.shape[1]} columns")
                self.populate_table()  # 填充表格
                self.populate_custom_page2()  # 填充CustomPage2
                self.ui.stackedWidget.setCurrentWidget(self.ui.page1)  # 切换到 page1 页面
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法加载Excel文件：{str(e)}")
        else:
            QMessageBox.information(self, "取消", "操作已取消")

    def populate_custom_page2(self):
        if self.uploaded_data is not None:
            self.custom_page2.clear_rows()  # 先清除现有行

            # 获取第一列的列名
            first_column_name = self.uploaded_data.columns[0]

            for col in self.uploaded_data.columns:
                # 跳过第一列的统计
                if col == first_column_name:
                    continue
                missing_count = self.uploaded_data[col].isnull().sum()

                if pd.api.types.is_numeric_dtype(self.uploaded_data[col]):
                    # 统计数字数据类型的均值、最小值和最大值
                    mean_value = self.uploaded_data[col].mean()
                    min_value = self.uploaded_data[col].min()
                    max_value = self.uploaded_data[col].max()
                    data_summary = f"均值({mean_value:.2f}), 最小值({min_value:.2f}), 最大值({max_value:.2f})"
                else:
                    # 统计非数字数据类型的唯一值及其计数
                    unique_values = self.uploaded_data[col].value_counts()
                    data_summary = ', '.join([f"{str(val)}({str(count)})" for val, count in unique_values.items()])
                # 使用最新的数据类型
                data_type = str(self.uploaded_data[col].dtype)

                self.custom_page2.add_row(
                    attr_name=col,
                    data_type=data_type,
                    missing_count=missing_count,
                    data_summary=data_summary
                )
        else:
            QMessageBox.warning(self, "No Data", "请先上传数据")

    def on_page_changed(self, index):
        print(f"Page changed to index: {index}")  # 打印页面索引
        print(f"Total pages: {self.ui.stackedWidget.count()}")  # 打印总页面数量
        page = self.ui.stackedWidget.widget(index)
        if page == self.ui.page1:
            self.select_button(self.ui.toolButtonP1)
        elif page == self.ui.page2:
            self.select_button(self.ui.toolButtonP2)
        elif page == self.ui.page3:
            self.select_button(self.ui.toolButtonP3)
    def check_page_data(self):
        if self.uploaded_data is not None:
            self.populate_table()

    # 在QTableWidget中展示数据
    def populate_table(self):
        if self.uploaded_data is not None:
            rows, cols = self.uploaded_data.shape
            self.ui.tableWidget.setRowCount(rows)
            self.ui.tableWidget.setColumnCount(cols)
            self.ui.tableWidget.setHorizontalHeaderLabels(self.uploaded_data.columns)

            for row in range(rows):
                for col in range(cols):
                    item = QTableWidgetItem(str(self.uploaded_data.iat[row, col]))
                    self.ui.tableWidget.setItem(row, col, item)

            self.ui.tableWidget.update()  # 强制刷新表格视图
        else:
            print("No data to populate")

    def show_page3(self):
        print("show_page3 called")
        # 检查数据是否已上传
        if self.uploaded_data is None or self.uploaded_data.empty:
            # 如果未上传数据或数据为空，弹出警告对话框并返回
            QMessageBox.warning(self, "警告", "请先上传有效数据！")
            return

        # 切换到 Page3 并绘制直方图
        self.ui.stackedWidget.setCurrentWidget(self.ui.page3)

        # 检查数据集是否包含至少一列
        if self.uploaded_data.shape[1] == 0:
            QMessageBox.warning(self, "警告", "数据集不包含任何列，无法绘制直方图！")
            return

        # 获取最后一列并绘制直方图
        last_column = self.uploaded_data.columns[-1]
        data = self.uploaded_data[last_column]

        # print(f"Data for histogram: {data.head()}")  # 打印数据检查内容
        # 清除当前图表
        self.custom_page3.clear_plot()
        # 绘制直方图
        self.custom_page3.plot_histogram(data)

    def visualize_column(self, attr_name):
        if self.uploaded_data is not None:
            column_data = self.uploaded_data[attr_name]
            print(f"可视化 {attr_name} 列的数据")
            # 切换到 page3
            # self.ui.stackedWidget.setCurrentWidget(self.custom_page3)
            # 切换到 page3
            self.select_page(self.ui.page3)
            # 使用 CustomPage3 的 plot_histogram 方法
            self.custom_page3.plot_histogram(column_data)





    def handle_close_event(self):
        # 定义文件路径和文件夹路径
        json_file_path = './model.json'
        # models_folder_path = './models'
        # # target_folder_path = './model'

        # # 检查 model 文件夹是否存在，不存在则创建
        # if not os.path.exists(target_folder_path):
        #     os.makedirs(target_folder_path)

        # 1. 删除 model.json 中 "state": "已创建" 的条目
        if os.path.exists(json_file_path):
            with open(json_file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)

            # 过滤掉 "state": "已创建" 的条目
            data = [entry for entry in data if entry.get('state') != '已创建']

            # 写回修改后的数据到 model.json 文件
            with open(json_file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=4)
            print(f"已删除 'state': '已创建' 的条目。")
        # 如果线程正在运行，优雅地退出
            # 检查并安全退出线程
        if hasattr(self.step_manager, 'thread') and self.step_manager.thread.isRunning():
            self.step_manager.thread.quit()  # 请求线程结束
            self.step_manager.thread.wait()  # 等待线程结束
            print("线程已安全退出。")
        # 读取 model.json 中所有的 ID
        # model_ids = []
        # if os.path.exists(json_file_path):
        #     with open(json_file_path, 'r', encoding='utf-8') as f:
        #         data = json.load(f)
        #     # 获取所有 "ID" 字段，并忽略空 ID
        #     model_ids = [entry.get("ID") for entry in data if entry.get("ID")]

        # 遍历 models 文件夹，找到匹配的文件并移动
        # if os.path.exists(models_folder_path):
        #     for filename in os.listdir(models_folder_path):
        #         # 去除文件的扩展名后，判断文件名是否在 ID 列表中
        #         file_id = os.path.splitext(filename)[0]  # 去除扩展名，获取文件名作为 ID
        #         if file_id in model_ids and filename.endswith(".pkl"):  # 只处理匹配的 .pkl 文件
        #             source_path = os.path.join(models_folder_path, filename)
        #             target_path = os.path.join(target_folder_path, filename)
        #
        #             # 移动文件
        #             shutil.move(source_path, target_path)
        #             print(f"已移动 {filename} 到 {target_folder_path}")

            # # 删除 models 文件夹中的所有文件
            # for file in os.listdir(models_folder_path):
            #     file_path = os.path.join(models_folder_path, file)
            #     if os.path.isfile(file_path):
            #         os.remove(file_path)
            #     elif os.path.isdir(file_path):
            #         shutil.rmtree(file_path)
            # print(f"已删除 models 文件夹中的所有模型。")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DataManagerApp()
    window.show()
    sys.exit(app.exec())

